js代码审计,找关键语句,发现event.js里边东西是最多的
找到了通关语句
他下边应该是就关键代码了,有一串16进制代码
解出来是base64,接着解 两次base64 解码,就得到了flag, 把前缀换成NSSCTF就ok
源代码:
error_reporting(0);
class catalogue{
public $class;
public $data;
public function __construct()
{
$this->class = "error";
$this->data = "hacker";
}
public function __destruct()
{
echo new $this->class($this->data);
}
}
class error{
public function __construct($OTL)
{
$this->OTL = $OTL;
echo ("hello ".$this->OTL);
}
}
class escape{
public $name = 'OTL';
public $phone = '123666';
public $email = '[email protected]';
}
function abscond($string) {
$filter = array('NSS', 'CTF', 'OTL_QAQ', 'hello');
$filter = '/' . implode('|', $filter) . '/i';
return preg_replace($filter, 'hacker', $string);
}
if(isset($_GET['cata'])){
if(!preg_match('/object/i',$_GET['cata'])){
unserialize($_GET['cata']);
}
else{
$cc = new catalogue();
unserialize(serialize($cc));
}
if(isset($_POST['name'])&&isset($_POST['phone'])&&isset($_POST['email'])){
if (preg_match("/flag/i",$_POST['email'])){die("nonono,you can not do that!");
}
$abscond = new escape();
$abscond->name = $_POST['name'];
$abscond->phone = $_POST['phone'];
$abscond->email = $_POST['email'];
$abscond = serialize($abscond);
$escape = get_object_vars(unserialize(abscond($abscond)));
if(is_array($escape['phone'])){
echo base64_encode(file_get_contents($escape['email']));
}
else{
echo "I'm sorry to tell you that you are wrong";
}
}
}
else{
highlight_file(__FILE__);
}
?>
PHP原生类的利用_慕晨sekurlsa的博客-CSDN博客
https://www.cnblogs.com/bcxc/articles/17052527.html (php原生类利用)
还是,要构造pop链,这里有一个利用点。
这里可以利用反序列化原生类
public function __destruct()
{
echo new $this->class($this->data);
}
}
这里可以实例化任意类,而且配合echo输出,那么自然想到就是原生类读文件了。这里利用FilesystemIterator来读取根目录有哪些文件。
还有一个利用点是file_get_contents()函数,读取文件,路径可控
if(is_array($escape['phone'])){
echo base64_encode(file_get_contents($escape['email']));
过滤点:
<1>对于第一个反序列化点
只对get传入的序列化字符串内容进行正则匹配,匹配object关键字,忽略大小写
<2>对于第二个文件读取点
(0)get要设置cata的值
(1)对post传入的email参数进行匹配,匹配关键字flag,忽略大小写
(2)对生成的catalogue序列化字段进行关键字的替换,关键字'NSS', 'CTF', 'OTL_QAQ', 'hello'替换成'hacker'
(3)post phone要传入一个数组
读到了文件是/flag,接着来
在根目录下有一个flag文件,我们利用SplFileObject这个原生类,同时他对object进行了过滤
利用编码绕过即可。
这里有一个知识点:
方便数据的传输,反序列化内容中大写的S表示字符串,可以识别内容里的十六进制
把序列化中的s变成大写就可以进行绕过,通过这个知识点,把object里边的一个字母变成16进制,就可以实现绕过,这里把b变成\62就可以
payload:
O:9:"catalogue":2:{s:5:"class";S:13:"SplFileO\62ject";s:4:"data";s:5:"/flag";}
得到flag
这里只是一种解法,还可以利用字符替换,进行字符串逃逸,然后利用file_get_contenes()进行文件读取
推荐一篇大佬的博客
nssprize5 - 刷题记录 |Yume Shoka = Xilzy's blog = Just have fun in cyberspace!
Linux内核:进程管理——进程文件系统 /proc详解 - 知乎
Linux下 /proc文件夹内容解析(/proc文件系统解析)_proc environ-CSDN博客
有一种非预期解法,就是利用/proc来读取环境变量,得到flag
在第一个链接来读取
发现能读取,那就读取环境变量得到flag file:///proc/1/environ
这是一个Linux系统特有的文件路径,其中的“/proc”目录是一个特殊的虚拟文件系统,提供了对内核数据结构的访问和控制。在这个目录下,“1”代表的是PID为1的进程(也就是init进程)。而“environ”文件则保存了该进程启动时设置的环境变量,以及之后在该进程中使用 putenv() 或者 setenv() 等相关函数修改的环境变量。
他在最后一个链接提到了/app ,应该是他源码存在的地方,尝试访问
发现读取不出来
/flag也是读取不出来,应该是有限制,利用url编码来读取
app/app.py -> %61%70%70/%61%70%70%2E%70%79 --> %2561%2570%2570/%2561%2570%2570%252E%2570%2579
成功读取到源码
#encoding:utf-8
import os
import re, random, uuid
from flask import *
from werkzeug.utils import *
import yaml
from urllib.request import urlopen
app = Flask(__name__)
random.seed(uuid.getnode())
app.config['SECRET_KEY'] = str(random.random()*233)
app.debug = False
BLACK_LIST=["yaml","YAML","YML","yml","yamiyami"]
app.config['UPLOAD_FOLDER']="/app/uploads"
@app.route('/')
def index():
session['passport'] = 'YamiYami'
return '''
Welcome to HDCTF2023 Read somethings
Here is the challenge Upload file
Enjoy it pwd
'''
@app.route('/pwd')
def pwd():
return str(pwdpath)
@app.route('/read')
def read():
try:
url = request.args.get('url')
m = re.findall('app.*', url, re.IGNORECASE)
n = re.findall('flag', url, re.IGNORECASE)
if m:
return "re.findall('app.*', url, re.IGNORECASE)"
if n:
return "re.findall('flag', url, re.IGNORECASE)"
res = urlopen(url)
return res.read()
except Exception as ex:
print(str(ex))
return 'no response'
def allowed_file(filename):
for blackstr in BLACK_LIST:
if blackstr in filename:
return False
return True
@app.route('/upload', methods=['GET', 'POST'])
def upload_file():
if request.method == 'POST':
if 'file' not in request.files:
flash('No file part')
return redirect(request.url)
file = request.files['file']
if file.filename == '':
return "Empty file"
if file and allowed_file(file.filename):
filename = secure_filename(file.filename)
if not os.path.exists('./uploads/'):
os.makedirs('./uploads/')
file.save(os.path.join(app.config['UPLOAD_FOLDER'], filename))
return "upload successfully!"
return render_template("index.html")
@app.route('/boogipop')
def load():
if session.get("passport")=="Welcome To HDCTF2023":
LoadedFile=request.args.get("file")
if not os.path.exists(LoadedFile):
return "file not exists"
with open(LoadedFile) as f:
yaml.full_load(f)
f.close()
return "van you see"
else:
return "No Auth bro"
if __name__=='__main__':
pwdpath = os.popen("pwd").read()
app.run(
debug=False,
host="0.0.0.0"
)
print(app.config['SECRET_KEY'])
需要做的事情就2件,因为提示在/boogipop做坏事,那么就需要伪造session,Yaml反序列化。
伪造session,需要知道secret_key,对secret_key的定义
app.config['SECRET_KEY'] = str(random.random()*233)
对于伪随机数,当seed固定时,生成的随机数是可以预测的,也就是顺序为固定的,所以只要知道seed的值即可。这里看到seed使用的uuid.getnode()函数,该函数用于获取Mac地址并将其转换为整数。所以我们还需要读一下Mac地址。
random.seed(uuid.getnode())
前提知识:
在 python 中使用 uuid 模块生成 UUID(通用唯一识别码)。可以使用 uuid.getnode() 方法来获取计算机的硬件地址,这个地址将作为 UUID 的一部分。
那么/sys/class/net/eth0/address,这个就是网卡的位置,读取他进行伪造即可。
读取到的mac地址,把他转换成10进制
2485376912964
构造secret_key 74.93509459398877
他的session形式
在进行伪造session,伪造好session之后就是进行反序列化了
eyJwYXNzcG9ydCI6IldlbGNvbWUgVG8gSERDVEYyMDIzIn0.ZTPnxA.HH4vFZIJQ3RCFrA5XkJcirW1oe4
PyYaml反序列化漏洞_snowlyzz的博客-CSDN博客
接下来就是构造并上传yml文件进行反序列化:
由于上传文件后缀设置了黑名单,所以直接采用.txt文件,那为什么.txt也能被当作.yaml来解析呢。猜测可能是:这里full_load调用了load函数,而load函数输入的是一个steam,也就是流,二进制文件,所以不管是什么后缀都无关紧要了。
上传成功后通过源码可知文件访问路径为/boogipop?file=/uploads/
expp.txt
接下来就是连接 然后进行查询,得到flag
改session
监听并连接成功