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 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187
| import argparse, json, time, random, requests
ABI = json.loads(r'''[{"constant":false,"inputs":[{"name":"_guess","type":"bool"}],"name":"flip","outputs":[{"name":"","type":"bool"},{"name":"","type":"string"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"consecutiveWins","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"verify","outputs":[{"name":"","type":"address"},{"name":"","type":"uint256"},{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"inputs":[{"name":"_key","type":"string"}],"payable":false,"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":false,"name":"","type":"address"},{"indexed":false,"name":"","type":"uint256"}],"name":"ConsecutiveWins","type":"event"}]''')
def norm_base(url: str) -> str: url = url.strip() if not url.endswith('/'): url += '/' return url
def get_block_number(base: str, group_id: int) -> int: r = requests.get(f"{base}{group_id}/web3/blockNumber", timeout=10) r.raise_for_status() data = r.json() return int(data) if isinstance(data, str) else int(data)
def trans_handle(base: str, group_id: int, user: str, contract: str, func: str, params: list): payload = { "groupId": str(group_id), "user": user, "contractName": "CoinFlip", "contractAddress": contract, "funcName": func, "funcParam": [str(p) for p in params], "contractAbi": ABI, "useCns": False } r = requests.post(f"{base}trans/handle", json=payload, timeout=25) r.raise_for_status() return r.json()
def extract_scalar(resp): """ 容错解析 WeBASE 的各种返回形态,尽量拿到标量(uint/string)。 """ if resp is None: return None if isinstance(resp, (int, float)): return resp if isinstance(resp, str): try: return int(resp, 0) except Exception: return resp if isinstance(resp, dict): for k in ["data", "result", "output", "ret", "returnData"]: if k in resp: v = resp[k] if isinstance(v, list) and v: first = v[0] if isinstance(first, dict) and "value" in first: fv = first["value"] try: return int(str(fv), 0) except Exception: return fv return extract_scalar(first) else: return extract_scalar(v) return resp if isinstance(resp, list) and resp: return extract_scalar(resp[0]) return resp
def get_wins(base, group_id, user, contract) -> int: resp = trans_handle(base, group_id, user, contract, "consecutiveWins", []) val = extract_scalar(resp) try: return int(val) except Exception: print("consecutiveWins 原始返回:", json.dumps(resp, ensure_ascii=False)) raise
def call_flip(base, group_id, user, contract, guess: bool): return trans_handle(base, group_id, user, contract, "flip", ["true" if guess else "false"])
def call_verify(base, group_id, user, contract): resp = trans_handle(base, group_id, user, contract, "verify", []) if isinstance(resp, dict): if "output" in resp and isinstance(resp["output"], list) and len(resp["output"]) >= 3: outs = resp["output"] winner = outs[0].get("value") streak = int(str(outs[1].get("value"))) key = outs[2].get("value") return winner, streak, key
d = resp.get("data") if isinstance(d, dict): winner = d.get("0") or d.get("winner") or d.get("address") or d.get("addr") streak = d.get("1") or d.get("streak") or d.get("uint256") key = d.get("2") or d.get("key") or d.get("string") if streak is not None: try: streak = int(str(streak)) except Exception: pass if winner or key: return winner, streak, key if isinstance(d, str): try: jd = json.loads(d) winner = jd.get("0") or jd.get("winner") streak = jd.get("1") or jd.get("streak") key = jd.get("2") or jd.get("key") if streak is not None: streak = int(str(streak)) return winner, streak, key except Exception: pass
print("verify 原始返回:", json.dumps(resp, ensure_ascii=False)) raise RuntimeError("无法从 verify 响应中解析三元组")
def main(): ap = argparse.ArgumentParser(description="XCTF CoinFlip Solver via WeBASE-Front") ap.add_argument("--base", required=True, help="WeBASE-Front 基础URL,如 http://host/WeBASE-Front/") ap.add_argument("--group", type=int, default=1, help="群组ID(默认1)") ap.add_argument("--contract", required=True, help="合约地址") ap.add_argument("--user", required=True, help="WeBASE-Front 本地用户地址(用于签名)") ap.add_argument("--delay", type=float, default=0.15, help="每次 flip 间隔秒数") ap.add_argument("--max-tries", type=int, default=4000, help="最大尝试次数") ap.add_argument("--alt", action="store_true", help="随机 True/False(默认交替)") args = ap.parse_args()
base = norm_base(args.base) print("[*] Base:", base) try: bn = get_block_number(base, args.group) print("[*] BlockNumber:", bn) except Exception as e: print("[!] 取块高失败(不影响刷题):", e)
try: wins = get_wins(base, args.group, args.user, args.contract) print(f"[*] 当前连胜: {wins}") except Exception as e: print("[!] 读取连胜失败:", e)
tries = 0 last = None while tries < args.max_tries: try: wins = get_wins(base, args.group, args.user, args.contract) print(f"[=] wins={wins}") if wins >= 10: print("[*] 达成 10 连胜,调用 verify() 取 key ...") winner, streak, key = call_verify(base, args.group, args.user, args.contract) print("[+] winner:", winner) print("[+] streak :", streak) print("[+] key :", key) return except Exception as e: print("[!] 读 wins 异常:", e)
guess = (tries % 2 == 0) if not args.alt else bool(random.getrandbits(1)) try: rec = call_flip(base, args.group, args.user, args.contract, guess) brief = {k: rec.get(k) for k in ["transactionHash", "txHash", "blockNumber", "code", "message"] if isinstance(rec, dict) and k in rec} print(f"[>] flip({guess}) -> {json.dumps(brief, ensure_ascii=False) if brief else str(rec)[:160]}") except Exception as e: print(f"[!] flip 异常:{e}")
tries += 1 time.sleep(args.delay)
print("[x] 未在 max-tries 内达成 10 连胜,可增大 --max-tries 或加大 --delay。")
if __name__ == "__main__": main()
|