web,forensics A cracked tails partition, a packet...
Criado em: 12 de abril de 2026
Respondido usando GPT-5.4 Thinking por Chat01
Criado em: 12 de abril de 2026
Respondido usando GPT-5.4 Thinking por Chat01
web,forensics
A cracked tails partition, a packet capture, and a darkweb criminal named purple. FYI, you'll need TOR to complete this challenge; keep legal and institutional regulations in mind.
这是一道ctf题目
pythonimport os, sqlite3, json, textwrap, re, struct, sys, hashlib, base64 paths = { "sqlite": "/mnt/data/brickroad/tails/enc/bookmarks/places.sqlite", "wal": "/mnt/data/brickroad/tails/enc/bookmarks/places.sqlite-wal", } for k,p in paths.items(): print(k, os.path.exists(p), os.path.getsize(p)) # connect readonly maybe WAL automatically? conn = sqlite3.connect(paths["sqlite"]) cur=conn.cursor() cur.execute("SELECT name FROM sqlite_master WHERE type='table'") print(cur.fetchall()[:20])
pythonimport os, sqlite3, json, re, textwrap, binascii for p in ["/mnt/data/places.sqlite", "/mnt/data/places.sqlite-wal", "/mnt/data/creds.hex"]: print(p, os.path.exists(p), os.path.getsize(p)) conn = sqlite3.connect("/mnt/data/places.sqlite") cur=conn.cursor() cur.execute("PRAGMA journal_mode") print("journal_mode", cur.fetchone()) cur.execute("SELECT name FROM sqlite_master WHERE type='table' ORDER BY 1") tables = [r[0] for r in cur.fetchall()] print(tables[:50], "count", len(tables))
pythonconn = sqlite3.connect("/mnt/data/places.sqlite") cur=conn.cursor() # recent places cur.execute(""" SELECT id, url, title, visit_count, last_visit_date FROM moz_places ORDER BY COALESCE(last_visit_date,0) DESC LIMIT 50 """) rows = cur.fetchall() rows[:10], len(rows) ([(1, 'http://2gzyxa5ihm7nsggfxnu52rck2vv4rvmdlkiu3zzui5du4xyclen53wid.onion/', None, 0, None), (2, 'http://pzhdfe7jraknpj2qgu5cz2u3i4deuyfwmonvzu5i3nyw4t4bmg7o5pad.onion/', None, 0, None), (3, 'http://rzuwtpc4wb3xdzrj3yeajsvm3fkq4vbeubm2tdxaqruzzzgs5dwemlad.onion/', None, 0, None), (4, 'about:manual', None, 0, None), (5, 'http://xmrhfasfg5suueegrnc4gsgyi2tyclcy5oz7f5drnrodmdtob6t2ioyd.onion/', None, 0, None), (6, 'http://v236xhqtyullodhf26szyjepvkbv6iitrhjgrqj4avaoukebkk6n6syd.onion/', None, 0, None), (7, 'https://donate.torproject.org/', None, 0, None), (8, 'http://yq5jjvr7drkjrelzhut7kgclfuro65jjlivyzfmxiq2kyv5lickrl4qd.onion/', None, 0, None), (9, 'http://yfudbrarr7smhd6x7hd7xkkdmcijy5d2i4soar3s2qoow7fg2n5l3dyd.onion/', None, 0, None)], 9)
pythonimport importlib, sys mods = ["scapy.all", "dpkt", "pyshark"] for m in mods: try: mod = importlib.import_module(m) print(m, "OK", mod) except Exception as e: print(m, "ERR", type(e).__name__, e)
pythonimport struct, collections, ipaddress, os, binascii, math, re, json pcap = "/mnt/data/brickroad/dist/packets.pcapng" # make readable copy maybe root file? let's just open; should be readable? use os.stat import os print(oct(os.stat(pcap).st_mode), os.path.getsize(pcap))
pythonimport os, struct, collections, ipaddress, binascii, re, json pcap = "/mnt/data/packets.pcapng" print(os.path.getsize(pcap)) # Pcapng parser SHB = 0x0A0D0D0A IDB = 0x00000001 EPB = 0x00000006 SPB = 0x00000003 ISB = 0x00000005 NRB = 0x00000004 def align4(n): return (n + 3) & ~3 interfaces = [] packets = [] with open(pcap, 'rb') as f: data = f.read() off=0 endian = '<' block_counts=collections.Counter() while off + 12 <= len(data): btype = struct.unpack_from('<I', data, off)[0] blen = struct.unpack_from('<I', data, off+4)[0] if blen < 12 or off+blen > len(data): # maybe wrong endian? break block = data[off:off+blen]
pythonimport struct, collections, ipaddress, binascii, re, json, math def parse_eth(pkt): if len(pkt) < 14: return None dst, src, etype = pkt[:6], pkt[6:12], struct.unpack('!H', pkt[12:14])[0] return {'dst':':'.join(f'{b:02x}' for b in dst), 'src':':'.join(f'{b:02x}' for b in src), 'etype':etype, 'payload':pkt[14:]} def parse_ipv4(buf): if len(buf)<20: return None vihl=buf[0] ver=vihl>>4; ihl=(vihl&0xf)*4 if ver!=4 or len(buf)<ihl: return None total=struct.unpack('!H', buf[2:4])[0] proto=buf[9] src=str(ipaddress.IPv4Address(buf[12:16])) dst=str(ipaddress.IPv4Address(buf[16:20])) return {'proto':proto,'src':src,'dst':dst,'payload':buf[ihl:
python# Summarize flows from collections import Counter flowc = Counter(flows) for flow, cnt in flowc.most_common(30): print(cnt, flow)
pythonhexs=open('/mnt/data/creds.hex').read().strip() b=bytes.fromhex(hexs) b, len(b) (b'\n\x07user_10\x12D\n \x8d\x14(Y\x0c\xb49\x06xoD\xc0l\xc01T9\xa6\x12\xca\xb6\xbeJ\xbe*:98\xe5|\x0f\xc8\x12 \x04\xe6?\xfdP\xe9eDmh7nG\xe4F\x0f\xe9\xe7U\xd6\xdd\x87\x8c\x04ZH\x8a\xedvNL\xd2', 79)
python# extract ascii payloads with printable content from tcp flows interesting=[] for iface_id,tsh,tsl,p in packets: eth=parse_eth(p) if not eth: continue net=parse_ipv4(eth['payload']) if eth['etype']==0x0800 else None if not net or net['proto']!=6: continue tcp=parse_tcp(net['payload']) if not tcp or not tcp['payload']: continue payload=tcp['payload'] if b'SSH-' in payload or b'HTTP' in payload or b'GET ' in payload or b'POST ' in payload or b'USER ' in payload or b'PASS ' in payload: interesting.append((net['src'],tcp['sport'],net['dst'],tcp['dport'],payload[:200])) interesting[:20], len(interesting) ([('2.57.122.192', 36516, '139.84.236.244', 22, b'SSH-2.0-PUTTY\r\n'), ('139.84.236.244', 22, '2.57.122.192', 36516, b'SSH-2.0-OpenSSH_9.6p1 Ubuntu-3ubuntu13.15\r\n'), ('139.84.236.244', 37224, '70.34.203.113', 1984, b'GET /1afaa1e32f63623160046882266183e1d134f008456ea99031ffdee773e1721c HTTP/1.1\r\nHost: 70.34.203.113:1984\r\nConnection: Upgrade\r\nUpgrade: websocket\r\nSec-WebSocket-Version: 13\r\nSec-WebSocket-Extensions: '), ('139.84.236.244', 37240, '70.34.203.113', 1984, b'GET /5b7197269725f041fa094bbf763cca8b3104a0902f2b984da61b0cbc6775a997 HTTP/1.1\r\nHost: 70.34.203.113:1984\r\nConnection: Upgrade\r\nUpgrade: websocket\r\nSec-WebSocket-Version: 13\r\nSec-WebSocket-Extensions: '), ('70.34.203.113', 1984, '139.84.236.244', 37224, b'HTTP/1.1 101 Switching Protocols\r\nconnection: upgrade\r\nupgrade: websocket\r\nsec-websocket-accept: 0UyQnU1XEqHk8PbIy0OaHAkP8Pw=\r\ndate: Sat, 11 Apr 2026 02:20:35 GMT\r\n\r\n'), ('70.34.203.113', 1984, '139.84.236.244', 37240, b'HTTP/1.1 101 Switching Protocols\r\nconnection: upgrade\r\nupgrade: websocket\r\nsec-websocket-accept: GfrBo/6GAxz6qZ4nOEvj9i+uRrc=\r\ndate: Sat, 11 Apr 2026 02:20:35 GMT\r\n\r\n'), ('139.84.236.244', 37246, '70.34.203.113', 1984, b'GET /8346251b1c5e53795cfa5aa4a14997365bcba0f1e1835e1fde09a0f663d25416 HTTP/1.1\r\nHost: 70.34.203.113:1984\r\nConnection: Upgrade\r\nUpgrade: websocket\r\nSec-WebSocket-Version: 13\r\nSec-WebSocket-Extensions: '), ('70.34.203.113', 1984, '139.84.236.244', 37246, b'HTTP/1.1 101 Switching Protocols\r\nconnection: upgrade\r\nupgrade: websocket\r\nsec-websocket-accept: SgxpLn4K5q4xxdqK3p7L56Tdq0I=\r\ndate: Sat, 11 Apr 2026 02:20:36 GMT\r\n\r\n'), ('139.84.236.244', 50294, '70.34.203.113', 1984, b'GET /1afaa1e32f63623160046882266183e1d134f008456ea99031ffdee773e1721c HTTP/1.1\r\nHost: 70.34.203.113:1984\r\nConnection: Upgrade\r\nUpgrade: websocket\r\nSec-WebSocket-Version: 13\r\nSec-WebSocket-Extensions: '), ('139.84.236.244', 50298, '70.34.203.113', 1984, b'GET /5517253bc07165c5b7878dc96debf38c06fed1c126c22925d28a2ac79cb89352 HTTP/1.1\r\nHost: 70.34.203.113:1984\r\nConnection: Upgrade\r\nUpgrade: websocket\r\nSec-WebSocket-Version: 13\r\nSec-WebSocket-Extensions: '), ('70.34.203.113', 1984, '139.84.236.244', 50294, b'HTTP/1.1 101 Switching Protocols\r\nconnection: upgrade\r\nupgrade: websocket\r\nsec-websocket-accept: zrf56977W7fmiFlyP75S7C8ehxo=\r\ndate: Sat, 11 Apr 2026 02:22:21 GMT\r\n\r\n'), ('70.34.203.113', 1984, '139.84.236.244', 50298, b'HTTP/1.1 101 Switching Protocols\r\nconnection: upgrade\r\nupgrade: websocket\r\nsec-websocket-accept: vJJB70argfCKh0RGb7ti/yB7foY=\r\ndate: Sat, 11 Apr 2026 02:22:21 GMT\r\n\r\n'), ('139.84.236.244', 50312, '70.34.203.113', 1984, b'GET /8346251b1c5e53795cfa5aa4a14997365bcba0f1e1835e1fde09a0f663d25416 HTTP/1.1\r\nHost: 70.34.203.113:1984\r\nConnection: Upgrade\r\nUpgrade: websocket\r\nSec-WebSocket-Version: 13\r\nSec-WebSocket-Extensions: '), ('70.34.203.113', 1984, '139.84.236.244', 50312, b'HTTP/1.1 101 Switching Protocols\r\nconnection: upgrade\r\nupgrade: websocket\r\nsec-websocket-accept: B7WNVJN0RmpXeWv+40f+D/VLWDA=\r\ndate: Sat, 11 Apr 2026 02:22:22 GMT\r\n\r\n'), ('195.178.110.15', 57186, '139.84.236.244', 22, b'SSH-2.0-PUTTY\r\n'), ('139.84.236.244', 22, '195.178.110.15', 57186, b'SSH-2.0-OpenSSH_9.6p1 Ubuntu-3ubuntu13.15\r\n'), ('139.84.236.244', 22, '80.94.92.165', 34144, b'SSH-2.0-OpenSSH_9.6p1 Ubuntu-3ubuntu13.15\r\n'), ('139.84.236.244', 22, '80.94.92.187', 44078, b'SSH-2.0-OpenSSH_9.6p1 Ubuntu-3ubuntu13.15\r\n'), ('80.94.92.187', 44078, '139.84.236.244', 22, b'SSH-2.0-Go\r\n'), ('2.57.121.118', 40982, '139.84.236.244', 22, b'SSH-2.0-PUTTY\r\n')], 64)
pythonconn = sqlite3.connect("/mnt/data/places.sqlite") cur = conn.cursor() # join bookmarks cur.execute(""" SELECT b.id, b.title, p.url, b.dateAdded, b.lastModified, b.type, b.parent, b.position FROM moz_bookmarks b LEFT JOIN moz_places p ON b.fk = p.id ORDER BY b.id """) rows = cur.fetchall() len(rows), rows[:20] (16, [(1, '', None, 1775925886077000, 1775926382735000, 2, 0, 0), (2, 'menu', None, 1775925886077000, 1775925888724000, 2, 1, 0), (3, 'toolbar', None, 1775925886077000, 1775926382735000, 2, 1, 1), (4, 'tags', None, 1775925886077000, 1775925886077000, 2, 1, 2), (5, 'unfiled', None, 1775925886077000, 1775925888714000, 2, 1, 3), (6, 'mobile', None, 1775925888515000, 1775925888714000, 2, 1, 4), (7, 'Tor Project Bookmarks', None, 1775925888724000, 1775925888724000, 2, 2, 0), (8, 'Learn more about Tor', 'http://2gzyxa5ihm7nsggfxnu52rck2vv4rvmdlkiu3zzui5du4xyclen53wid.onion/', 1775925888724000, 1775925888724000, 1, 7, 0), (9, 'The Tor Blog', 'http://pzhdfe7jraknpj2qgu5cz2u3i4deuyfwmonvzu5i3nyw4t4bmg7o5pad.onion/', 1775925888724000, 1775925888724000, 1, 7, 1), (10, 'Tor Support portal - Get help', 'http://rzuwtpc4wb3xdzrj3yeajsvm3fkq4vbeubm2tdxaqruzzzgs5dwemlad.onion/', 1775925888724000, 1775925888724000, 1, 7, 2), (11, 'Tor Browser Manual', 'about:manual', 1775925888724000, 1775925888724000, 1, 7, 3), (12, 'Tor Community - Get Involved', 'http://xmrhfasfg5suueegrnc4gsgyi2tyclcy5oz7f5drnrodmdtob6t2ioyd.onion/', 1775925888724000, 1775925888724000, 1, 7, 4), (13, 'Tor Project Forum', 'http://v236xhqtyullodhf26szyjepvkbv6iitrhjgrqj4avaoukebkk6n6syd.onion/', 1775925888724000, 1775925888724000, 1, 7, 5), (14, 'Donate - Keep Tor Strong', 'https://donate.torproject.org/', 1775925888724000, 1775925888724000, 1, 7, 6), (15, 'Circumvent Censorship, Get Bridges', 'http://yq5jjvr7drkjrelzhut7kgclfuro65jjlivyzfmxiq2kyv5lickrl4qd.onion/', 1775925888724000, 1775925888724000, 1, 7, 7), (16, 'Brick Road', 'http://yfudbrarr7smhd6x7hd7xkkdmcijy5d2i4soar3s2qoow7fg2n5l3dyd.onion/', 1775926382735000, 1775926382735000, 1, 3, 0)])
pythonconn = sqlite3.connect("/mnt/data/places.sqlite") cur = conn.cursor() for q in [ "SELECT id, url, title, visit_count, frecency, last_visit_date FROM moz_places ORDER BY id", "SELECT id, place_id, visit_date, visit_type, from_visit, opener_visit FROM moz_historyvisits ORDER BY id LIMIT 50", "SELECT * FROM moz_inputhistory LIMIT 50", "SELECT * FROM moz_keywords LIMIT 50", "SELECT * FROM moz_meta LIMIT 50", ]: print("QUERY", q) try: cur.execute(q) rows=cur.fetchall() for r in rows[:20]: print(r) print("count", len(rows)) except Exception as e: print("ERR", e) print()
pythonfor table in ['moz_historyvisits','moz_historyvisits_extra','moz_places_metadata','moz_places_extra']: print(table) cur.execute(f"PRAGMA table_info({table})") print(cur.fetchall()) cur.execute(f"SELECT * FROM {table} LIMIT 5") rows=cur.fetchall() for r in rows: print(r) print()
python# Collect payload bytes per tcp flow for 70.34.203.113:1984 from collections import defaultdict streams = defaultdict(bytearray) for iface_id,tsh,tsl,p in packets: eth=parse_eth(p) if not eth: continue net=parse_ipv4(eth['payload']) if eth['etype']==0x0800 else None if not net or net['proto']!=6: continue tcp=parse_tcp(net['payload']) if not tcp or not tcp['payload']: continue if tcp['sport']==1984 or tcp['dport']==1984: key=(net['src'],tcp['sport'],net['dst'],tcp['dport']) streams[key].extend(tcp['payload']) for k,v in streams.items(): print(k, len(v), repr(bytes(v[:200]))) print()
pythonimport datetime, math def moz_to_dt(us): return datetime.datetime(1970,1,1)+datetime.timedelta(microseconds=us) for val in [1775925888724000,1775926382735000]: print(val, moz_to_dt(val))
python# show full http requests/responses for 1984 for k,v in streams.items(): if k[1]!=1984 and k[3]!=1984: continue try: txt = bytes(v).decode('latin1', errors='replace') except: txt = repr(bytes(v[:500])) print("FLOW",k) # print until end headers idx = txt.find('\r\n\r\n') print(txt[:idx+4 if idx!=-1 else 500]) print('-'*60)
pythondef websocket_frames(data, from_client=False, max_frames=20): i=0; out=[] while i+2<=len(data) and len(out)<max_frames: b1=data[i]; b2=data[i+1] fin=b1>>7; rsv=(b1>>4)&0x7; opcode=b1&0x0f mask=b2>>7; plen=b2&0x7f; i+=2 if plen==126: if i+2>len(data): break plen=struct.unpack('!H', data[i:i+2])[0]; i+=2 elif plen==127: if i+8>len(data): break plen=struct.unpack('!Q', data[i:i+8])[0]; i+=8 mkey=None if mask: if i+4>len(data): break mkey=data[i:i+4]; i+=4 if i+plen>len(data
pythonimport hashlib, itertools, base64, binascii vals = { 'user_10': b'user_10', 'Brick Road': b'Brick Road', 'brick road': b'brick road', 'purple': b'purple', 'yfud...': b'yfudbrarr7smhd6x7hd7xkkdmcijy5d2i4soar3s2qoow7fg2n5l3dyd.onion', 'hash1': bytes.fromhex('8d1428590cb43906786f44c06cc0315439a612cab6be4abe2a3a3938e57c0fc8'), 'hash2': bytes.fromhex('04e63ffd50e965446d68376e47e4460fe9e755d6dd878c045a488aed764e4cd2'), } targets = [ '1afaa1e32f63623160046882266183e1d134f008456ea99031ffdee773e1721c', '5b7197269725f041fa094bbf763cca8b3104a0902f2b984da61b0cbc6775a997', '8346251b1c5e53795cfa5aa4a14997365bcba0f1e1835e1fde09a0f663d25416', '5517253bc07165c5b7878dc96debf38c06fed1c126c22925d28a2ac79cb89352', ] for name,v in vals.items(): print(name, 'sha256', hashlib.sha256(v).hexdigest()) print(name, 'sha256hexstr', hashlib.sha256(v.hex().encode()).hexdigest())
pythonfrom collections import Counter macs=Counter() etypes=Counter() for _,_,_,p in packets: eth=parse_eth(p) if not eth: continue macs[eth['src']]+=1; macs[eth['dst']]+=1 etypes[eth['etype']]+=1 print("MACs", macs.most_common()) print("etypes", etypes)
pythonimport importlib, os, sys for m in ['nacl.bindings','cryptography.hazmat.primitives.asymmetric.x25519','ed25519']: try: mod=importlib.import_module(m) print("OK",m) except Exception as e: print("ERR",m,e)
pythonfrom cryptography.hazmat.primitives.asymmetric import x25519 from cryptography.hazmat.primitives import serialization b=bytes.fromhex(open('/mnt/data/creds.hex').read().strip()) # parse protobuf manually assert b[0]==0x0a and b[1]==7 user=b[2:9].decode() inner=b[11:11+68] # maybe field2 print(user, inner.hex()) assert inner[0]==0x0a and inner[1]==0x20 and inner[34]==0x12 and inner[35]==0x20 a=inner[2:34]; c=inner[36:68] print(len(a),len(c),a.hex(),c.hex()) try: priv = x25519.X25519PrivateKey.from_private_bytes(a) pub = priv.public_key().public_bytes(encoding=serialization.Encoding.Raw, format=serialization.PublicFormat.Raw)
pythonfrom cryptography.hazmat.primitives.asymmetric import ed25519 from cryptography.hazmat.primitives import serialization a=bytes.fromhex('8d1428590cb43906786f44c06cc0315439a612cab6be4abe2a3a3938e57c0fc8') c=bytes.fromhex('04e63ffd50e965446d68376e47e4460fe9e755d6dd878c045a488aed764e4cd2') try: priv=ed25519.Ed25519PrivateKey.from_private_bytes(a) pub=priv.public_key().public_bytes(encoding=serialization.Encoding.Raw, format=serialization.PublicFormat.Raw) print(pub.hex(), pub==c) except Exception as e: print("ed25519 fail", e)
python# Re-parse packets with seq numbers for port 1984 flow def parse_tcp_full(buf): if len(buf)<20: return None sport,dport,seq,ack,offres,flags,win,chk,urg = struct.unpack('!HHIIBBHHH', buf[:20]) off=(offres>>4)*4 return {'sport':sport,'dport':dport,'flags':flags,'payload':buf[off:], 'off':off, 'seq':seq, 'ack':ack} flow_packets=[] for iface_id,tsh,tsl,p in packets: eth=parse_eth(p) if not eth: continue net=parse_ipv4(eth['payload']) if eth['etype']==0x0800 else None if not net or net['proto']!=6: continue tcp=parse_tcp_full(net['payload']) if not tcp: continue if (net['src'],tcp['sport'],net['dst'],tcp['dport']) == ('139.84.236.244',50298,'70.34.203.113',1984) or \ (net['src'],tcp['sport'],net['dst'],tcp['dport']) == ('70.34.203.113',1984,'139.84.236.244',50298): flow_packets.append((net['src'],tcp['sport'],net['dst'],tcp['dport'],tcp['seq'],tcp['ack'],tcp['flags'],tcp['payload'])) len(flow_packets), flow_packets[:5] (74, [('139.84.236.244', 50298, '70.34.203.113', 1984, 4156248989, 0, 2, b''), ('70.34.203.113', 1984, '139.84.236.244', 50298, 1235898971, 4156248990, 18, b''), ('139.84.236.244', 50298, '70.34.203.113', 1984, 4156248990, 1235898972, 16, b''), ('139.84.236.244', 50298, '70.34.203.113', 1984, 4156248990, 1235898972, 24, b'GET /5517253bc07165c5b7878dc96debf38c06fed1c126c22925d28a2ac79cb89352 HTTP/1.1\r\nHost: 70.34.203.113:1984\r\nConnection: Upgrade\r\nUpgrade: websocket\r\nSec-WebSocket-Version: 13\r\nSec-WebSocket-Extensions: permessage-deflate; client_max_window_bits\r\nSec-WebSocket-Key: 4eV1omnBT1qD3Wyv6k/NVQ==\r\n\r\n'), ('70.34.203.113', 1984, '139.84.236.244', 50298, 1235898972, 4156249281, 16, b'')])
pythonfrom collections import defaultdict def reassemble(direction_packets): # direction_packets: list of (seq, payload) segs={} for seq,payload in direction_packets: if not payload: continue # keep longest for same seq if seq not in segs or len(payload) > len(segs[seq]): segs[seq]=payload # naive merge based on increasing seq starting from min out=bytearray() if not segs: return bytes(out) sorted_items=sorted(segs.items()) cur=None for seq,payload in sorted_items: if cur is None: out.extend(payload); cur=seq+len(payload); start=seq else:
pythonidx=client_data.find(b'\r\n\r\n') body=client_data[idx+4:] frames=websocket_frames(body,max_frames=30) print("frames", [(f['opcode'],f['len'],f['mask']) for f in frames[:15]]) for i,f in enumerate(frames[:10]): print(i, f['len'], f['payload'][:16].hex(), repr(f['payload'][:60]))
我先把这题里离线就能确定的关键线索整理出来。
1. Tails 持久化分区里最关键的异常项
Brick Roadhttp://yfudbrarr7smhd6x7hd7xkkdmcijy5d2i4soar3s2qoow7fg2n5l3dyd.onion/Brick Road 书签时间约是 2026-04-11 16:53:02 UTC,晚了大约 8 分钟。这基本可以当成题目的主入口。
2. creds.hex 不是明文密码,而是一个 protobuf 风格的凭据结构
我把它解出来后,能稳定拿到:
user_108d1428590cb43906786f44c06cc0315439a612cab6be4abe2a3a3938e57c0fc8
04e63ffd50e965446d68376e47e4460fe9e755d6dd878c045a488aed764e4cd2
也就是说,这更像是站点登录所需的密钥材料/设备凭据,不像传统的 username:password。
3. 抓包里最值得盯的不是 SSH 扫描,而是一个 WebSocket 服务
packets.pcapng 里最有价值的应用层明文握手是:
139.84.236.244 -> 70.34.203.113:19841afaa1e32f63623160046882266183e1d134f008456ea99031ffdee773e1721c5b7197269725f041fa094bbf763cca8b3104a0902f2b984da61b0cbc6775a9978346251b1c5e53795cfa5aa4a14997365bcba0f1e1835e1fde09a0f663d254165517253bc07165c5b7878dc96debf38c06fed1c126c22925d28a2ac79cb89352另外还有到 213.32.23.113:9100、176.123.1.217:9001 的连接,看起来很像 Tor 相关中继流量。大量对 22 端口的 SSH 连接更像背景噪声/暴露主机常见扫描,不像这题真正的主线。
4. 目前最像正解的路线 这题的主线大概率是:
creds.hex 拿到 user_10 和对应的二进制凭据。Brick Road,用这些凭据登录或恢复会话。我在离线证据里没有直接找到 purple 这个字符串,所以它大概率不在 zip 里明文出现,而是在你连上 Brick Road 之后、站内页面/账户内容里。
5. 你本地接下来该怎么做
按照 Tor Project,.onion 服务本来就只能通过 Tor 访问;Tor Project 也明确说 Tor2web 会移除 Tor 的保护,不如直接用 Tor Browser。(Tor Community)
所以建议你在隔离环境里做下面几步:
http://yfudbrarr7smhd6x7hd7xkkdmcijy5d2i4soar3s2qoow7fg2n5l3dyd.onion/user_106. 我对这题目前的结论 你现在已经拿到了这题最关键的三个 IOC:
yfudbrarr7smhd6x7hd7xkkdmcijy5d2i4soar3s2qoow7fg2n5l3dyd.onionuser_10这通常已经足够把题推进到 Tor 站内阶段了。
把你打开后的登录页、页面源码,或者站内和 purple 相关的截图贴上来,我就继续帮你把后半段走完。