2020 DASCTF 6th Re all wp

Jun 26, 2020

这次比赛re不难,做的很爽,misc/smb因为卡了一波半角全角然后就没拿到一血orz..本来要写misc wp的发现群里都写完了,那就先来一波re吧。

本人其实是misc师傅...基本什么都看一眼,跟专业逆向肯定是天地之差...wp写的需要改进的地方希望大佬多指点。

RE

521

逆的时候看到异或了,然后就蒙只异或,通过构造部分相同的输入发现那部分输出也是相同的,没把加密部分搅到一块去,大概率是明文异或了。

exp:

def xor(x,y):
    result = ""
    for i in range(len(x)):
        result += chr(ord(x[i])^ord(y[i]))
    return result


enc = ""
enc += p64(0x6c8e072a35235980)
enc += p64(0x7df45d5aee345023)
enc += p64(0xe0766f80db58e7b1)
enc += p64(0x301b4c2e821f0d50)
enc += p64(0x000000229b9e8271)

dec = "Nep{0123456789ABCDEFFEDCBA9876543210}"

flag = "8059233522738D1A515D30E85726F607C6925EDC831F7692250F65FB2E4D6B450387E99F2200".decode("hex")
cipher = xor(enc[:37],dec)
print xor(cipher,flag)

DDoll

感觉这个题开了挺多混淆,至少ida下面看的乱乱的,还好是win下的逆向,直接拉到od里准备搜一波字符串。

刚开始进去搜字符串还没有,多跟几步基本就有了。

输入部分和success与fail输出都有了,直接去判断flag正确与否的地方找一下有没有什么特别的。

je就是关键跳了,上面最近的call是用来判断的。在ida里查看一下发现就是纯比较,然后就看call 0x891122时的栈来确定传参的两个地址。之后传一个比较长的字符串,再通过传参把加密后的字符串取出来,与原字符串xor一下就能把xor的key给套出来,直接xor目标字符串就OK了。

PS: ida里的rebase还是挺好用的。od里的地址有时候跟ida里的不一样,直接rebase。

ida edit->segments->rebase program,之后打开od的memory map(alt+m),找pe文件头,ida的base地址直接设成文件头的地址两边地址就能一块用了。

exp:

enc = [0xC6,0xA5,0x04,0x53,0x33,0xC3,0xC8,0x9E,0x2F,0x8F,0x44,0xE0,0x9D,0x24,0x2F,0x28,0xE4,0xDC,0xDB,0x34,0x78,0xB8,0x4C,0x38]
flag=[0xC6,0xA5,0x04,0x53,0x17,0xF6,0xCF,0xAA,0x21,0xAF,0x5C,0xE0,0x9F,0x17,0x2F,0x74,0x88,0xA6,0x8B,0x72,0x3F,0xE5,0x1B,0x75,0xD1]

def xor(x,y):
    result = ""
    for i in range(len(x)):
        result += chr(ord(x[i])^ord(y[i]))
    return result

dec = "Nep{fuckyouall1234567890}"
cipher = xor("".join([chr(i) for i in enc]),dec)
print xor(cipher,"".join([chr(i) for i in flag]))

Magia

扔到ida里在main下面看,题目给了三个数组,分别是flag[i]^flag[len-i],flag[i]&flag[len-i]和flag[i]&0xf的结果。不想手算直接扔z3。发现z3解出来flag看起来怪怪的,结合题目的小写+下划线限定一波就是正确输入了。接下来跟进0x403000,发现对flag进行了逐位处理,最后把结果写到了ebp-0x4c,数据区跟过去就看到32个字符了。

PS1: ida7.2比ida7.0要先进一点,能准确定位更多程序的main函数地址,但ida7.2 32bit没hexray...所以我为了确定main函数先拖到ida7.2 64bit,确定完main在哪再回ida7.0 32bit看hexray。

PS2: 为啥我就敢判断ebp-0x4c有东西,因为从上到下看了一圈,mov [寄存器+寄存器+数值],* 只有这个这一个地方,可以猜edx是当前计数寄存器(不管是正着数的还是倒着数的),结果就对了。

eazy_maze

送分题...ida里面给了一个100个0和O组成的数组,然后又定义了上下左右键,直接对着数组shift+e导出之后10个10个分开,然后照着地图敲就行了,初始点在(0,0)

brainbreaker

找到main之后就看到div 0了,直接去找seh链。

od: 查看->SEH链 越靠前的函数越先catch exception,所以第一个就是接下来的流程了。

接下来搜一波字符串,又发现了right和wrong..直接跟过去

老套路了,直接比较,然后上面就大概率有关键函数..

在ida里看,发现是一堆数据,对着数据按c就变成code了。

发现是switch case,上面又有奇怪的一串东西在往里面读,估计是vm,然后就下个断点验证一下。然而下断到jmp程序就直接退出了。怀疑有检测断点的机制,下断点给ExitProcess。

看最后的栈帧在这,ida跟过去一看,就是这个位置有个验证

应该是计算内存"哈希",毕竟od下断点都是直接在内存里插int 3,计算一下就露馅了。直接把jnz patch成nop就OK。剩下的断都正常断了。

分析发现这个vm有两个寄存器,然后opcode种类少,操作好看,直接分析opcode再来一波z3就完事。(我真是爱死z3了23333)

本来是要opcode+表里的东西才能对switch做选择的,没想到ida直接就把什么分支对应什么字符给整出来了,太厉害了。

exp:

from z3 import *

a1 = [BitVec('flag%d' % i,9) for i in range(100)]
s = Solver()
a1.reverse()

result = [0xFA,0x2B,0x94,0xEA,0x63,0xA8,0xC4,0x13,0x84,0xE7,0xA7,0xDA,0xD4,0x2D,0xAE,0xBE]

opcode = "****--:~+******^.~+*****:*+++++^.-:///--*^.:///---^.,:~-----^>,:~-/++++++++++++^>,:~+****--^>+*******://++++++++++^:,^>-:///^:,^>+******://-^:,^>-/+://+++++++^:,^>-/----:,^>-/+:/+++++^:,^>-:++****+^:,^>-/+:~+**++^:,^>+****:*^:,^>-:++****++++^:,^>-/---:,^>+*****://+^:,^>-/+:///+^:,^>,"
ax = 0
bx = 0
fin=[0 for i in range(100)]
fin[0] = 1
for i in opcode:
    o = ord(i)
    if o == 60:
        ax -= 1
        # print "dec ax","ax = ",ax
    if o == 62:
        ax += 1
        # print "inc ax","ax = ",ax
    if o == 46:
        continue
        # print "put %c fin[{}]".format(ax)
        # print chr(fin[ax])
    if o == 44:
        fin[ax] = a1.pop()
        # print "get %2x fin[{}]".format(ax)
        # break
    if o == 43:
        # print "inc fin[{}]".format(ax)
        fin[ax] += 1
    if o == 45:
        # print "dec fin[{}]".format(ax)
        fin[ax] -= 1
    if o == 42:
        # print "double fin[{}]".format(ax)
        fin[ax] *= 2
    if o == 47:
        # print "shr fin[{}]".format(ax)
        fin[ax] >>= 1
    if o == 94:
        # print "xor fin[{}],bx".format(ax)
        fin[ax] ^= bx
    if o == 58:
        # print "mov bx,fin[{}]".format(ax)
        bx = fin[ax]
    if o == 126:
        # print "check ax == 32;mov fin[{}],0".format(ax)
        if ax == 32:
            break
        fin[ax] = 0
    fin[ax] &= 0xff
    ax &= 0xff
    bx &= 0xff
        
for i in range(len(result)):
    s.add(result[i] == fin[i])

print s.check()
answer = s.model()
# flag = "".join([hex(answer[i].as_long())[2:] for i in a1])
# print flag
print answer

PS1: 注意python里的符号问题,vm里寄存器操作是无符号的,python直接加减是有符号的,需要&0xff来转换成无符号。

PS2: 由于只有BitVec支持位操作,而BitVec是无符号的,请时刻注意这点。当你发现计算的结果和你预期不符时,可以试着把BitVec扩大一位来解决问题。这个题没碰到这个情况。

pyCharm

参照https://www.52pojie.cn/thread-912103-1-1.html

提一句找长度,我也不知道长度在哪,但是我len(code)之后小端编码就可直接在pyc里面搜16进制了。

这就很简单了。

T0p_Gear

题目描述都是“签 到 题”了...的确很签到。

直接开ida,发现有3个check,而且基本没什么混淆,还是直接明文strcmp的...直接断在3个strcmp答案就出来了。

其他方向参考

misc: http://www.ga1axy.top/index.php/archives/42/

re: https://badmonkey.site/archives/dasctf-2020-6.html

部分web: https://www.gem-love.com/ctf/2401.html

部分pwn: https://blog.csdn.net/weixin_44145820/article/details/106974426

文档: https://shimo.im/docs/ywRvpRW3DkgqDgqP

Great! You've successfully subscribed.
Great! Next, complete checkout for full access.
Welcome back! You've successfully signed in.
Success! Your account is fully activated, you now have access to all content.
京ICP备18053813号-1