題目連結

https://learn.cylabacademy.org/library/472

題目解法

下載題目原始碼後進行分析,發現當程式遇到 CROWD 開頭的行時會將使用者輸入的內容會被寫回 song_lines[lip]。此外程式在處理每一行時,會先用 split(';') 將其分割成多個子指令進行循環。
因此當我們輸入

;RETURN 0

程式內部的反應如下:

  1. song_lines[lip] 會變成 ‘Crowd: ;RETURN 0’。
  2. for line in song_lines[lip].split(’;’): 會將這一行切成兩部分:
    • 第一部分:‘Crowd: ’ -> 進入 else 分支,列印內容並將 lip 加 1。
    • 第二部分:‘RETURN 0’ -> 進入 elif re.match(r"RETURN [0-9]+", line): 分支。
  3. 此時 lip 會被設為 int(‘0’),也就是 0。
  4. while 迴圈繼續執行,下一次迭代就會從 song_lines[0] 開始列印,這時隱藏在開頭的 flag 就會被直接輸出到螢幕上。
    webshell:~$ nc verbal-sleep.picoctf.net 53947
    Command line wizards, we’re starting it right,
    Spawning shells in the terminal, hacking all night.
    Scripts and searches, grep through the void,
    Every keystroke, we're a cypher's envoy.
    Brute force the lock or craft that regex,
    Flag on the horizon, what challenge is next?
    
    We’re flag hunters in the ether, lighting up the grid,
    No puzzle too dark, no challenge too hid.
    With every exploit we trigger, every byte we decrypt,
    We’re chasing that victory, and we’ll never quit.
    Crowd: ;RETURN 0
    
    Echoes in memory, packets in trace,
    Digging through the remnants to uncover with haste.
    Hex and headers, carving out clues,
    Resurrect the hidden, it's forensics we choose.
    Disk dumps and packet dumps, follow the trail,
    Buried deep in the noise, but we will prevail.
    
    We’re flag hunters in the ether, lighting up the grid,
    No puzzle too dark, no challenge too hid.
    With every exploit we trigger, every byte we decrypt,
    We’re chasing that victory, and we’ll never quit.
    Crowd: 
    Pico warriors rising, puzzles laid bare,
    Solving each challenge with precision and flair.
    With unity and skill, flags we deliver,
    The ether’s ours to conquer, picoCTF{70637h3r_f0r3v3r_836f0788}
    
    
    [REFRAIN]
    We’re flag hunters in the ether, lighting up the grid,
    No puzzle too dark, no challenge too hid.
    With every exploit we trigger, every byte we decrypt,
    We’re chasing that victory, and we’ll never quit.
    Crowd: 
    Pico warriors rising, puzzles laid bare,
    Solving each challenge with precision and flair.
    With unity and skill, flags we deliver,
    The ether’s ours to conquer, picoCTF{706-------------------788}
    

題目原始碼

lyric-reader.py

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
import re
import time


# Read in flag from file
flag = open('flag.txt', 'r').read()

secret_intro = \
'''Pico warriors rising, puzzles laid bare,
Solving each challenge with precision and flair.
With unity and skill, flags we deliver,
The ether’s ours to conquer, '''\
+ flag + '\n'


song_flag_hunters = secret_intro +\
'''

[REFRAIN]
We’re flag hunters in the ether, lighting up the grid,
No puzzle too dark, no challenge too hid.
With every exploit we trigger, every byte we decrypt,
We’re chasing that victory, and we’ll never quit.
CROWD (Singalong here!);
RETURN

[VERSE1]
Command line wizards, we’re starting it right,
Spawning shells in the terminal, hacking all night.
Scripts and searches, grep through the void,
Every keystroke, we're a cypher's envoy.
Brute force the lock or craft that regex,
Flag on the horizon, what challenge is next?

REFRAIN;

Echoes in memory, packets in trace,
Digging through the remnants to uncover with haste.
Hex and headers, carving out clues,
Resurrect the hidden, it's forensics we choose.
Disk dumps and packet dumps, follow the trail,
Buried deep in the noise, but we will prevail.

REFRAIN;

Binary sorcerers, let’s tear it apart,
Disassemble the code to reveal the dark heart.
From opcode to logic, tracing each line,
Emulate and break it, this key will be mine.
Debugging the maze, and I see through the deceit,
Patch it up right, and watch the lock release.

REFRAIN;

Ciphertext tumbling, breaking the spin,
Feistel or AES, we’re destined to win.
Frequency, padding, primes on the run,
Vigenère, RSA, cracking them for fun.
Shift the letters, matrices fall,
Decrypt that flag and hear the ether call.

REFRAIN;

SQL injection, XSS flow,
Map the backend out, let the database show.
Inspecting each cookie, fiddler in the fight,
Capturing requests, push the payload just right.
HTML's secrets, backdoors unlocked,
In the world wide labyrinth, we’re never lost.

REFRAIN;

Stack's overflowing, breaking the chain,
ROP gadget wizardry, ride it to fame.
Heap spray in silence, memory's plight,
Race the condition, crash it just right.
Shellcode ready, smashing the frame,
Control the instruction, flags call my name.

REFRAIN;

END;
'''

MAX_LINES = 100

def reader(song, startLabel):
  lip = 0
  start = 0
  refrain = 0
  refrain_return = 0
  finished = False

  # Get list of lyric lines
  song_lines = song.splitlines()
  
  # Find startLabel, refrain and refrain return
  for i in range(0, len(song_lines)):
    if song_lines[i] == startLabel:
      start = i + 1
    elif song_lines[i] == '[REFRAIN]':
      refrain = i + 1
    elif song_lines[i] == 'RETURN':
      refrain_return = i

  # Print lyrics
  line_count = 0
  lip = start
  while not finished and line_count < MAX_LINES:
    line_count += 1
    for line in song_lines[lip].split(';'):
      if line == '' and song_lines[lip] != '':
        continue
      if line == 'REFRAIN':
        song_lines[refrain_return] = 'RETURN ' + str(lip + 1)
        lip = refrain
      elif re.match(r"CROWD.*", line):
        crowd = input('Crowd: ')
        song_lines[lip] = 'Crowd: ' + crowd
        lip += 1
      elif re.match(r"RETURN [0-9]+", line):
        lip = int(line.split()[1])
      elif line == 'END':
        finished = True
      else:
        print(line, flush=True)
        time.sleep(0.5)
        lip += 1



reader(song_flag_hunters, '[VERSE1]')