dalctf{y311e8wi11_4m4eypt_32tni274} - flag này kho...
Criado em: 7 de junho de 2026
Respondido usando GPT-5.5 Thinking por Chat01
Criado em: 7 de junho de 2026
Respondido usando GPT-5.5 Thinking por Chat01
dalctf{y311e8wi11_4m4eypt_32tni274} - flag này khong đúng
Flag format: dalctf{...}
file output.txt + alphabet.txt là của cùng 1 challenge
tìm flag rồi viết write up chi tiết kèm lệnh + script đã dùng
pythonfrom pathlib import Path bits=Path('/mnt/data/output.txt').read_text().strip() lines=Path('/mnt/data/alphabet.txt').read_text().splitlines() items=[] for i,line in enumerate(lines): if not line.strip(): continue parts=line.split() ch=parts[0] freq=float(parts[1]) items.append((ch,freq,i)) len(bits), bits[:50], len(items), items[:5], items[-5:] (192, '10111010101101100110111111001101111110111101101011', 41, [('z', 0.00021, 0), ('j', 0.0003, 1), ('q', 0.00033, 2), ('x', 0.00051, 3), ('{', 0.001, 4)], [('0', 0.0336, 36), ('e', 0.03609, 37), ('3', 0.064, 38), ('2', 0.0808, 39), ('1', 0.0908, 40)])
pythonimport heapq, itertools def huffman_codes(items, tie='index', zero_left=True): # items (ch, freq, index) heap=[] counter=itertools.count() for ch,f,idx in items: if tie=='index': key=idx elif tie=='counter': key=next(counter) heap.append((f,key, {'ch':ch,'left':None,'right':None})) heapq.heapify(heap) nextkey=max(x[1] for x in heap)+1 if heap else 0 while len(heap)>1: f1,k1,n1=heapq.heappop(heap); f2,k2,n2=heapq.heappop(heap) if zero_left: node={'ch':None,'left':n1,'right':n2} else: node={'ch':None,'left':n2,'right':n1} heapq.heappush(heap,(f1+f2,nextkey,node)); nextkey+=1 root=heap
pythonfrom PIL import Image import numpy as np, matplotlib.pyplot as plt, os, math img=Image.open('/mnt/data/ScratchingAround.png').convert('RGBA') img.size (679, 54)
pythonimport numpy as np arr=np.array(img) # white/nonblack mask maybe alpha>0 and RGB>100 mask=(arr[:,:,3]>0)&(arr[:,:,:3].sum(axis=2)>300) col_counts=mask.sum(axis=0) # find gaps cols with no pixels gaps=np.where(col_counts==0)[0] # intervals of nonzero intervals=[] in_run=False start=0 for i,c in enumerate(col_counts): if c>0 and not in_run: start=i; in_run=True if (c==0 or i==len(col_counts)-1) and in_run: end=i-1 if c==0 else i intervals.append((start,end)) in_run=False intervals[:20], len(intervals) ([(5, 21), (27, 43), (49, 52), (54, 61), (67, 83), (89, 100), (102, 105), (111, 114), (121, 123), (129, 145), (151, 167), (173, 176), (179, 189), (195, 211), (217, 221), (223, 226), (232, 248), (254, 270), (276, 292), (298, 314)], 38)
pythonruns=intervals def combine(runs, max_gap): out=[] s,e=runs[0] for ns,ne in runs[1:]: if ns-e-1 <= max_gap: e=ne else: out.append((s,e)); s,e=ns,ne out.append((s,e)) return out for gap in range(0,10): ints=combine(runs,gap) print(gap,len(ints),ints[:10], 'avg width', sum(e-s+1 for s,e in ints)/len(ints))
pythons='dalctf{y311e8wi11_4m4eypt_32tni274}' len(s), len(s[7:-1]), s[7:-1] (35, 27, 'y311e8wi11_4m4eypt_32tni274')
python# create ascii art downsample? print crop with x coords around first 130 mask=(arr[:,:,3]>0)&(arr[:,:,:3].sum(axis=2)>300) # crop rows 0-54 cols 0-130; but need show as chars for y in range(0,54,2): line='' for x in range(0,130): if mask[y:y+2,x].any(): line+='#' else: line+=' ' print(line.rstrip())
pythonfor gap in [1,2,3,4]: ints=combine(runs,gap) print('gap',gap,'len',len(ints)) print(ints)
pythonsum(f for ch,f,i in items) 0.745
pythonfrom pathlib import Path data=Path('/mnt/data/ScratchingAround.png').read_bytes() data[:8], len(data) (b'\x89PNG\r\n\x1a\n', 3439)
pythonimport struct, zlib, binascii pos=8 chunks=[] while pos<len(data): length=struct.unpack(">I",data[pos:pos+4])[0]; typ=data[pos+4:pos+8].decode('latin1') payload=data[pos+8:pos+8+length]; crc=data[pos+8+length:pos+12+length] chunks.append((typ,length,payload[:50])) pos += 12+length chunks[:20], pos, len(data) ([('IHDR', 13, b'\x00\x00\x02\xa7\x00\x00\x006\x08\x06\x00\x00\x00'), ('sRGB', 1, b'\x00'), ('gAMA', 4, b'\x00\x00\xb1\x8f'), ('pHYs', 9, b'\x00\x00\x0e\xc3\x00\x00\x0e\xc3\x01'), ('IDAT', 3332, b'x^\xed\xdd\xbf\x8b\x1b\xe9\x19\x07\xf0\xaf\x93@\x9a-R\x18R\xea\\\x1c+\xc2\x14)R\xb8\x13n\xbcW\xd8\xd5\x88\xe3\x9a\x80*\xabH\x0c\x017\x82\x85\x90F \x02'), ('IEND', 0, b'')], 3439, 3439)
pythonimport numpy as np from PIL import Image img=Image.open('/mnt/data/ScratchingAround.png').convert('RGBA') arr=np.array(img) # unique colors colors=np.unique(arr.reshape(-1,4), axis=0) len(colors), colors[:10], colors[-10:] (40, array([[ 39, 39, 39, 255], [ 39, 39, 83, 255], [ 39, 39, 122, 255], [ 39, 83, 83, 255], [ 39, 83, 159, 255], [ 39, 122, 122, 255], [ 39, 122, 159, 255], [ 39, 122, 192, 255], [ 83, 39, 39, 255], [ 83, 39, 83, 255]], dtype=uint8), array([[224, 224, 159, 255], [224, 255, 224, 255], [224, 255, 255, 255], [255, 192, 122, 255], [255, 192, 159, 255], [255, 224, 159, 255], [255, 224, 192, 255], [255, 255, 192, 255], [255, 255, 224, 255], [255, 255, 255, 255]], dtype=uint8))
pythonfrom PIL import ImageOps img=Image.open('/mnt/data/ScratchingAround.png').convert('RGBA') for name,im in [('orig',img),('flip_lr',ImageOps.mirror(img)),('flip_tb',ImageOps.flip(img)),('rot180',img.rotate(180,expand=True))]: im.save(f'/mnt/data/{name}.png') <<ImageDisplayed>>
pythondef huff(items, tiekey_func, internal_key_strategy='sumkey', orient='small0'): import heapq heap=[] for ch,f,idx in items: heap.append((f, tiekey_func(ch,f,idx), {'ch':ch,'left':None,'right':None})) heapq.heapify(heap) nextkey=0 while heap: nextkey=max(nextkey, heap[0][1]+1 if isinstance(heap[0][1],(int,float)) else 0) break counter=1000000 while len(heap)>1: f1,k1,n1=heapq.heappop(heap); f2,k2,n2=heapq.heappop(heap) if orient=='small0': left,right=n1,n2 elif orient=='small1': left,right=n2,n1 elif orient=='lexleft': # no left,right=n1,n2 if internal_key_strategy=='counter': k=counter; counter+=1 elif internal_key_strategy=='minchar': # collect min char def chars(n): return [n['ch']] if n['ch'] is not None else chars(n['left'])+chars(n['right']) k=min(chars(left)+chars(right)) elif internal_key_strategy=='idxmin': pass heapq.heappush(heap,(f1+f2,k, {'ch':None,'left':left,'right':right})) root=heap[0][2] return decode(bits,root)[0], decode(bits,root)[1] variants=[] for tie_name,tie in [ ('input', lambda ch,f,idx: idx), ('revinput', lambda ch,f,idx: -idx), ('char', lambda ch,f,idx: ch), ('ord', lambda ch,f,idx: ord(ch)), ('revord', lambda ch,f,idx: -ord(ch)), ('freqstr', lambda ch,f,idx: (str(f),ch)), ]: for istr in ['counter','minchar']: for orient in ['small0','small1']: try: s,ok=huff(items,tie,istr,orient) variants.append((tie_name,istr,orient,ok,s)) except Exception as e: variants.append((tie_name,istr,orient,False,str(e))) for v in variants: if v[3] and v[4].startswith('dalctf'): print(v[:4], v[4], len(v[4])) print('all ok count', sum(1 for v in variants if v[3])) for v in variants[:20]: print(v)
pythonfrom dataclasses import dataclass, field import heapq, itertools @dataclass(order=True) class HNode: freq: float key: str symbol: str = field(compare=False, default=None) left: object = field(compare=False, default=None) right: object = field(compare=False, default=None) symbols: str = field(compare=False, default='') def build(nodes_key='minchar'): heap=[] for ch,f,i in items: heapq.heappush(heap,HNode(f,ch,ch,None,None,ch)) merges=[] while len(heap)>1: a=heapq.heappop(heap); b=heapq.heappop(heap) syms=a.symbols+b.symbols key=min(syms) if nodes_key=='minchar' else ''.join(sorted(syms)) parent=HNode(a.freq+b.freq,key,None,a,b,syms) merges.append((a.freq,a.key,a.symbols,b.freq,b.key,b.symbols,parent.freq,parent.key,parent.symbols)) heapq.heappush(heap,parent) return heap[0], merges root,merges=build('minchar') codes={} def walk(n,p=''): if n.symbol is not None: codes[n.symbol]=p else: walk(n.left,p+'0'); walk(n.right,p+'1') walk(root) decoded,ok=decode(bits, {'ch':None}) if False else ('',True) def decode2(bits, root): n=root; out=[] for b in bits: n = n.left if b=='0' else n.right if n.symbol is not None: out.append(n.symbol); n=root return ''.join(out), n is root s,ok=decode2(bits,root) s, ok, len(s), codes ('dalctf{y0u_wi11_3ncrypt_4lw4y$}', True, 31, {'h': '00000', 'r': '00001', 'e': '0001', 'u': '001000', 'b': '0010010', 'k': '00100110', '_': '00100111', 's': '00101', '6': '00110', '8': '00111', '2': '010', '9': '01100', 'n': '01101', 'i': '01110', '$': '011110', 'p': '0111110', 'g': '0111111', '1': '100', 'o': '10100', 'a': '10101', 'l': '101100', 'w': '1011010', 'y': '1011011', 'd': '101110', 'v': '10111100', 'x': '1011110100', 'q': '10111101010', 'z': '101111010110', 'j': '101111010111', '{': '1011110110', '}': '1011110111', 'f': '1011111', '5': '11000', 't': '11001', '!': '11010', '7': '110110', 'm': '1101110', 'c': '1101111', '4': '11100', '0': '11101', '3': '1111'})
pythonfor keytype in ['minchar','sortedchars']: root,_=build('minchar' if keytype=='minchar' else 'sortedchars') s,ok=decode2(bits,root) print(keytype,s,ok)
python# generate tokenization for correct and wrong candidates def tokenize(bits, root): toks=[]; cur=root; acc='' for b in bits: acc+=b cur=cur.left if b=='0' else cur.right if cur.symbol is not None: toks.append((cur.symbol, acc)); acc=''; cur=root return toks root_correct,_=build('minchar') toks_correct=tokenize(bits, root_correct) # wrong root from input/counter but HNode? implement quick root_wrong,_= None,None # Use previous dict node codes_wrong,root_d=huffman_codes(items, zero_left=True) # make decoder tokenization for dict root def toks_dict(bits, root): toks=[]; n=root; acc='' for b in bits: acc+=b n=n['left'] if b=='0' else n['right'] if n['ch'] is not None: toks.append((n['ch'],acc)); acc=''; n=root return toks toks_wrong=toks_dict(bits, root_d) print(len(toks_correct), ''.join(c for c,_ in toks_correct)) print(toks_correct[:12]) print(len(toks_wrong), ''.join(c for c,_ in toks_wrong)) print(toks_wrong[:12])
textdalctf{y0u_wi11_3ncrypt_4lw4y$}
output.txt là bitstream nhị phân, còn alphabet.txt là bảng ký tự + trọng số/tần suất để dựng Huffman tree. (output.txt) (alphabet.txt)
Chuỗi sai:
textdalctf{y311e8wi11_4m4eypt_32tni274}
xuất hiện khi dựng Huffman tree với tie-break theo thứ tự dòng trong alphabet.txt.
Nhưng Huffman không duy nhất khi có các node cùng trọng số. Challenge này cần tie-break ổn định theo ký tự, cụ thể là sort theo:
text(freq, smallest_character_in_subtree)
Khi dùng tie-break đúng, cùng bitstream sẽ decode ra flag hợp lệ:
textdalctf{y0u_wi11_3ncrypt_4lw4y$}
bashcd /mnt/data python3 solve.py
Output:
text[input-order tie break] dalctf{y311e8wi11_4m4eypt_32tni274} [lexicographic tie break] dalctf{y0u_wi11_3ncrypt_4lw4y$}
python#!/usr/bin/env python3 from dataclasses import dataclass, field from heapq import heappush, heappop @dataclass(order=True) class Node: freq: float key: str left: object = field(compare=False, default=None) right: object = field(compare=False, default=None) ch: str = field(compare=False, default=None) chars: str = field(compare=False, default='') def load_alphabet(path): items = [] with open(path, 'r', encoding='utf-8') as f: for line in f: line = line.strip() if not line: continue ch, freq = line.split() items.append((ch, float(freq))) return items def build_tree(items, tie_break='lexicographic'): heap = [] for i, (ch, freq) in enumerate(items): if tie_break == 'input_order': key = f'{i:04d}' else: key = ch heappush(heap, Node(freq=freq, key=key, ch=ch, chars=ch)) serial = len(heap) while len(heap) > 1: a = heappop(heap) b = heappop(heap) merged_chars = a.chars + b.chars if tie_break == 'input_order': key = f'{serial:04d}' serial += 1 else: # Mấu chốt của challenge: # nếu freq bằng nhau, node nào có ký tự nhỏ hơn sẽ được ưu tiên. key = min(merged_chars) heappush( heap, Node( freq=a.freq + b.freq, key=key, left=a, right=b, chars=merged_chars ) ) return heap[0] def decode(bits, root): out = [] cur = root for bit in bits: if bit not in '01': continue cur = cur.left if bit == '0' else cur.right if cur.ch is not None: out.append(cur.ch) cur = root if cur is not root: raise ValueError('Dangling bits at the end of the stream') return ''.join(out) def main(): bits = open('output.txt', 'r', encoding='utf-8').read().strip() items = load_alphabet('alphabet.txt') wrong = decode(bits, build_tree(items, tie_break='input_order')) print('[input-order tie break] ', wrong) correct = decode(bits, build_tree(items, tie_break='lexicographic')) print('[lexicographic tie break]', correct) if __name__ == '__main__': main()
Mình cũng lưu script ở đây: solve.py