CISCN2023 Quals Reverse Writeup

发布时间 2023-05-29 16:49:36作者: SinkDev

打了两天,第一天出了俩,第二天就出了一个(还不是 Android,只能说非常离谱

ezbyte

看师傅们的 Writeup 都说是 DWARF Expression, 可以直接用 readelf -wf 就行,我比赛的时候没看出来,直接动态调试分析的。

这里看到把输入压栈,在栈上下内存断点

image

直接断到虚拟机里了,然后动态调试即可

image

给出一个非常有限的反汇编器

import ctypes

ins = [0x10, 0xA2, 0xE8, 0x91, 0xD3, 0xF8, 0xDA, 0xE1, 0x85, 0x79,
       0x10, 0xFB, 0xDD, 0xEB, 0xDE, 0xBB, 0xF6, 0xEF, 0x9E, 0x1A,
       0x10, 0xF8, 0xEE, 0xAC, 0x04,
       0x7F, 0x00,  # load ?
       0x22,  # add
       0x27,  # xor
       0x27,  # xor
       0x10, 0xDC, 0xCF, 0xF0, 0x87, 0xA4, 0xE4, 0xA1, 0x83, 0x22,
       0x10, 0xED, 0xC0, 0xE1, 0x8D, 0x92, 0xA8, 0xEC, 0x9F, 0x0F,
       0x10, 0xF8, 0xA6, 0x5C,
       0x7E, 0x00,
       0x22,
       0x27,
       0x27,
       0x10, 0xF1, 0x95, 0xDC, 0xD0, 0xAB, 0x9E, 0x84, 0xFF, 0x75,
       0x10, 0xE3, 0x87, 0xDD, 0xD6, 0xF8, 0xB2, 0x8F, 0xE5, 0x10, 0x10, 0xE5, 0xC7, 0xA3, 0x04,
       0x7D, 0x00,
       0x22,
       0x27,
       0x27,
       0x10, 0xCF, 0xB6, 0x91, 0xCA, 0xB6, 0xBD, 0xEE, 0xA7, 0x24,
       0x10, 0xA7, 0x99, 0xA5, 0x80, 0xE5, 0xF1, 0xF7, 0x96, 0x11,
       0x10, 0x83, 0xC3, 0x73,
       0x7C, 0x00,
       0x22,
       0x27,
       0x27,
       0x22,
       0x22,
       0x22]



def handle_10(ins_buf, offset):
    r = ctypes.c_uint64(0)
    n = 0
    while True:
        i = ins_buf[offset]
        offset += 1
        v = (i & 0x7f) << n
        n += 7
        r.value |= v
        if i & 0x80 == 0:
            break
    return r.value, offset


i = 0
while i < len(ins):
    if ins[i] == 0x10:
        i += 1
        v, i = handle_10(ins, i)
        print(f"push {hex(v)}")
    elif 0x70 < ins[i] <= 0x8F:
        print(f'load input_part[{ins[i] - 0x7C}]')
        i += 2
    elif ins[i] == 0x22:
        print("push(pop() + pop())")
        i += 1
    elif ins[i] == 0x27:
        print("push(pop() ^ pop())")
        i += 1

使用 z3 求解即可

import z3
import struct

flag = [z3.BitVec(f'x_{i}', 64) for i in range(4)]

solver = z3.Solver()
solver.add((flag[3] + 0x8b3778) ^ 0x1a3dbfb3bbdaeefb == 0x790b86d78a647422)
solver.add((flag[2] + 0x171378) ^ 0xf3fb14121b8606d == 0x2206872240fc27dc)
solver.add((flag[1] + 0x88e3e5) ^ 0x10ca3d978ad743e3 == 0x75fe10f2ba170af1)
solver.add((flag[0] + 0x1ce183) ^ 0x112ddf8e50094ca7 == 0x244fb9eb69445b4f)

if solver.check() == z3.sat:
    m = solver.model()

    flagStr = ''.join(struct.pack("<Q", m[flag[i]].as_long()).decode() for i in range(4))
    print('flag{' + flagStr + '3861}')

moveAside

mov 混淆。开始注册了俩异常处理,第一个是调用函数的代码,第二个是就是下面的代码块,在调用函数的异常处理handler下断点,发现是使用 strcmp 逐位对比的 字符串,直接在strcmp 下断点,拉置换表即可。

拉表用的是 IDA Pro 的脚本

from idaapi import *
import time

f = open(r'C:\Users\Sink\Desktop\ctf\a.txt', 'w')
i = 0
while True:
    espVal = get_reg_val('esp')
    oAddr = get_dword(espVal + 4)
    tAddr = get_dword(espVal + 8)
    o = get_byte(oAddr)
    t = get_byte(tAddr)
    if o != t:
        patch_byte(oAddr, t)
    print(f'{i}: o = {hex(o)}, t = {hex(t)}')
    f.write(f'{i}: o = {hex(o)}, t = {hex(t)}\n')
    f.flush()
    i += 1
    continue_process()
    time.sleep(1)
    if i == 48:
        break
    wait_for_next_event(WFNE_SUSP, -1)

babyRE

青少年编程,很简单,就不解释了

sercet = [102, 10, 13, 6, 28, 74, 3, 1, 3, 7, 85, 0, 4, 75, 20, 92, 92, 8, 28, 25, 81, 83, 7, 28, 76, 88, 9, 0, 29, 73,
          0, 86, 4, 87,87, 82, 84, 85, 4, 85, 87, 30]

for i in range(1, len(sercet)):
    sercet[i] ^= sercet[i -1]
print(''.join(chr(c) for c in sercet))

ezAndroid

一直觉得是 RCE,到最后结束了我也没想明白,我为啥RCE不了,别人告诉我是目录穿越(出题人,你太卑鄙了,头套必须给你拽掉,狠狠的打你脸

首先,通过文件名、文件大小、没有生成 odex 优化,定位到 cpeweb.apk, jadx 打开发现有 AndroidX 库,这是个Android 4.4 的系统,肯定就是他了!

先说 RCE,UpgradeWebServer 这里

image

image

静默安装,安装后打开,接收器在 SystemUI.apk

image

image

不太理解为啥 RCE 不了。

再说,目前成功的做法目录穿越,很明显的路径拼接,可惜我是个逆向,可惜有个更明显的 RCE

image

flag 存放在根目录,所以 http://47.104.67.173:8000/tmp/img/../../../flag 即可

flutterror

不会!!!!!!