Mas0n
to be reverse engineer🐧
翻车鱼

第四届浙江省大学生网络与信息安全竞赛(预赛)

第四届浙江省大学生网络与信息安全竞赛(预赛)

今年省赛比去年难度大点,用的安恒的平台,前2小时原地坐牢。体验了一波flag交不上,附件下不了。后期有了些体验感……有个吐槽点就是不能联网……unicorn的题一出我直接傻眼,本地没有头文件,根本不知道常量符号。

Rev

crackPYC

手工反编译字节码

翻译即可

# -*- coding:utf-8 -*-
"""
@Author: Mas0n
@File: zjctf1.py
@Time: 2021-10-23 12:38
@Desc: It's all about getting better.
"""

def keyinit():
    num = 0
    key = []
    for i in range(8):
        num = (num - 7508399208111569251) % 4294967295
        key.append((num >> 24))
    print(key)
    return key


str = "0" * 32
text = [108, 17, 42, 226, 158, 180, 96, 115, 64, 24, 38, 236, 179, 173, 34, 22, 81, 113, 38, 215, 165, 135, 68, 7, 119, 97, 45, 254, 250, 172, 43, 62]
st = list(str)

key = keyinit()

for i in range(32):
    st[i] = ((text[i]) ^ key[i % len(key)])

print("".join([chr(i) for i in st]))
print(st)

Triple Language

赛时没网,赛后复现吧……套娃unicorn题

也算是入门unicorn了

先看第一个sub_1400012B0

看到第一个约束条件

  LOBYTE(v11) = raw[6] - raw[14];
  BYTE1(v11) = raw[7] - raw[15];
  BYTE2(v11) = raw[8] - raw[16];
  BYTE3(v11) = raw[9] - raw[17];
  BYTE4(v11) = raw[10] - raw[18];
  BYTE5(v11) = raw[11] - raw[19];
  BYTE6(v11) = raw[12] - raw[20];
  HIBYTE(v11) = raw[13] - raw[21];
  if ( v11 == 0x3EBB0EFAF301FCi64 )
      ……

而后进入unicorn执行

https://cdn.shi1011.cn/2021/11/e0c1516cc40a541093cc3a57983b183e.png?imageMogr2/format/webp/interlace/0/quality/90|watermark/2/text/wqlNYXMwbg/font/bXN5aGJkLnR0Zg/fontsize/14/fill/IzMzMzMzMw/dissolve/80/gravity/southeast/dx/5/dy/5

简单分析以下各参数的常量

可以知道运行架构为MIPS X86,dump下代码,放IDA,选择小端序

https://cdn.shi1011.cn/2021/11/e1f1f59985a62cba02c05a8856cb6115.png?imageMogr2/format/webp/interlace/0/quality/90|watermark/2/text/wqlNYXMwbg/font/bXN5aGJkLnR0Zg/fontsize/14/fill/IzMzMzMzMw/dissolve/80/gravity/southeast/dx/5/dy/5

根据unicorn相关传参手动创建ROM

https://cdn.shi1011.cn/2021/11/b06dc74a67f38df7665a2cc6bad88752.png?imageMogr2/format/webp/interlace/0/quality/90|watermark/2/text/wqlNYXMwbg/font/bXN5aGJkLnR0Zg/fontsize/14/fill/IzMzMzMzMw/dissolve/80/gravity/southeast/dx/5/dy/5

转换成代码后,逻辑还是很清楚的

https://cdn.shi1011.cn/2021/11/ccfa68749cc2af1997cdf5121c6276f9.png?imageMogr2/format/webp/interlace/0/quality/90|watermark/2/text/wqlNYXMwbg/font/bXN5aGJkLnR0Zg/fontsize/14/fill/IzMzMzMzMw/dissolve/80/gravity/southeast/dx/5/dy/5

其中t1~t6对应的是输入的前6个字节raw[0:6]byte_11000则对应之前uc_mem_write所加载的zjgcjy

进入hook回调verify1,会发现其在对应地址获取t0~t6寄存器的值并进行check

前六字节

# Part1
raw1 = [0] * 6
key = [ord(i) for i in "zjgcjy"]
result = [0x2F2E, 0x282A, 0x2C42, 0x2A8A, 0x13E0, 0x36D4]
for i in range(6):
    raw1[i] = result[i] // key[i]

flag1 = "".join([chr(i) for i in raw1])

在MIPS代码执行完毕后,会取出t0~t8寄存器的值进行check

https://cdn.shi1011.cn/2021/11/a0c35b12427f4eb4b70520b5c969264a.png?imageMogr2/format/webp/interlace/0/quality/90|watermark/2/text/wqlNYXMwbg/font/bXN5aGJkLnR0Zg/fontsize/14/fill/IzMzMzMzMw/dissolve/80/gravity/southeast/dx/5/dy/5

结合上面得到的第一个约束条件,和MIPS汇编,求出之后的8字节

import z3


checkBox = [194, 195, 215, 196, 218, 165, 160, 0xBE]
checkBox2 = list(int.to_bytes(0x3EBB0EFAF301FC, length=8, byteorder="little"))
raw2 = [z3.BitVec(f"a{str(i)}", 8) for i in range(8)]
raw3 = [z3.BitVec(f"b{str(i)}", 8) for i in range(8)]
slo = z3.Solver()
for i in range(8):
    slo.add(raw2[i] + raw3[i] == checkBox[i])
    slo.add(raw2[i] - raw3[i] == checkBox2[i])


assert slo.check() == z3.sat
rlt = slo.model()
flag2 = "".join([chr(rlt.eval(i).as_long()) for i in raw2])
flag3 = "".join([chr(rlt.eval(i).as_long()) for i in raw3])

# print(flag1 + flag2 + flag3)

#   LOBYTE(v11) = raw[6] - raw[14];
#   BYTE1(v11) = raw[7] - raw[15];
#   BYTE2(v11) = raw[8] - raw[16];
#   BYTE3(v11) = raw[9] - raw[17];
#   BYTE4(v11) = raw[10] - raw[18];
#   BYTE5(v11) = raw[11] - raw[19];
#   BYTE6(v11) = raw[12] - raw[20];
#   HIBYTE(v11) = raw[13] - raw[21];
# t1 == 194 && t2 == 195 && t3 == 215 && t4 == 196 && t5 == 218 && t6 == 165 && t7 == 160 && t8 == 0xBE

而后是第二部分sub_140001A90

首先校验了长度,而后进行check

https://cdn.shi1011.cn/2021/11/8d40083c048adee314fbae9b98921554.png?imageMogr2/format/webp/interlace/0/quality/90|watermark/2/text/wqlNYXMwbg/font/bXN5aGJkLnR0Zg/fontsize/14/fill/IzMzMzMzMw/dissolve/80/gravity/southeast/dx/5/dy/5

代码如下

__int64 sub_1400019F0()
{
  __int64 v0; // rcx
  unsigned int v1; // er8
  char *i; // r9
  char v3; // al
  char Destination[8]; // [rsp+20h] [rbp-28h] BYREF
  __int16 v6; // [rsp+28h] [rbp-20h]

  *(_QWORD *)Destination = 0i64;
  v6 = 0;
  strncpy(Destination, input, 4ui64);
  v0 = 0xFFFFFFFFFFFFFFFFui64;
  do
    ++v0;
  while ( Destination[v0] );
  v1 = -1;
  for ( i = Destination; (_DWORD)v0; LODWORD(v0) = v0 - 1 )
  {
    v3 = *i++;
    v1 = (v1 >> 8) ^ dword_1400034B0[(unsigned __int8)(v3 ^ v1)];
  }
  return ~v1;
}

这里首先能够想到爆破

# Part2
import string
import itertools


dword_1400034B0 = [0x00000000, 0xF26B8303, 0xE13B70F7, 0x1350F3F4, 0xC79A971F, 0x35F1141C, 0x26A1E7E8, 0xD4CA64EB, 0x8AD958CF, 0x78B2DBCC, 0x6BE22838, 0x9989AB3B, 0x4D43CFD0, 0xBF284CD3, 0xAC78BF27, 0x5E133C24, 0x105EC76F, 0xE235446C, 0xF165B798, 0x030E349B, 0xD7C45070, 0x25AFD373, 0x36FF2087, 0xC494A384, 0x9A879FA0, 0x68EC1CA3, 0x7BBCEF57, 0x89D76C54, 0x5D1D08BF, 0xAF768BBC, 0xBC267848, 0x4E4DFB4B, 0x20BD8EDE, 0xD2D60DDD, 0xC186FE29, 0x33ED7D2A, 0xE72719C1, 0x154C9AC2, 0x061C6936, 0xF477EA35, 0xAA64D611, 0x580F5512, 0x4B5FA6E6, 0xB93425E5, 0x6DFE410E, 0x9F95C20D, 0x8CC531F9, 0x7EAEB2FA, 0x30E349B1, 0xC288CAB2, 0xD1D83946, 0x23B3BA45, 0xF779DEAE, 0x05125DAD, 0x1642AE59, 0xE4292D5A, 0xBA3A117E, 0x4851927D, 0x5B016189, 0xA96AE28A, 0x7DA08661, 0x8FCB0562, 0x9C9BF696, 0x6EF07595, 0x417B1DBC, 0xB3109EBF, 0xA0406D4B, 0x522BEE48, 0x86E18AA3, 0x748A09A0, 0x67DAFA54, 0x95B17957, 0xCBA24573, 0x39C9C670, 0x2A993584, 0xD8F2B687, 0x0C38D26C, 0xFE53516F, 0xED03A29B, 0x1F682198, 0x5125DAD3, 0xA34E59D0, 0xB01EAA24, 0x42752927, 0x96BF4DCC, 0x64D4CECF, 0x77843D3B, 0x85EFBE38, 0xDBFC821C, 0x2997011F, 0x3AC7F2EB, 0xC8AC71E8, 0x1C661503, 0xEE0D9600, 0xFD5D65F4, 0x0F36E6F7, 0x61C69362, 0x93AD1061, 0x80FDE395, 0x72966096, 0xA65C047D, 0x5437877E, 0x4767748A, 0xB50CF789, 0xEB1FCBAD, 0x197448AE, 0x0A24BB5A, 0xF84F3859, 0x2C855CB2, 0xDEEEDFB1, 0xCDBE2C45, 0x3FD5AF46, 0x7198540D, 0x83F3D70E, 0x90A324FA, 0x62C8A7F9, 0xB602C312, 0x44694011, 0x5739B3E5, 0xA55230E6, 0xFB410CC2, 0x092A8FC1, 0x1A7A7C35, 0xE811FF36, 0x3CDB9BDD, 0xCEB018DE, 0xDDE0EB2A, 0x2F8B6829, 0x82F63B78, 0x709DB87B, 0x63CD4B8F, 0x91A6C88C, 0x456CAC67, 0xB7072F64, 0xA457DC90, 0x563C5F93, 0x082F63B7, 0xFA44E0B4, 0xE9141340, 0x1B7F9043, 0xCFB5F4A8, 0x3DDE77AB, 0x2E8E845F, 0xDCE5075C, 0x92A8FC17, 0x60C37F14, 0x73938CE0, 0x81F80FE3, 0x55326B08, 0xA759E80B, 0xB4091BFF, 0x466298FC, 0x1871A4D8, 0xEA1A27DB, 0xF94AD42F, 0x0B21572C, 0xDFEB33C7, 0x2D80B0C4, 0x3ED04330, 0xCCBBC033, 0xA24BB5A6, 0x502036A5, 0x4370C551, 0xB11B4652, 0x65D122B9, 0x97BAA1BA, 0x84EA524E, 0x7681D14D, 0x2892ED69, 0xDAF96E6A, 0xC9A99D9E, 0x3BC21E9D, 0xEF087A76, 0x1D63F975, 0x0E330A81, 0xFC588982, 0xB21572C9, 0x407EF1CA, 0x532E023E, 0xA145813D, 0x758FE5D6, 0x87E466D5, 0x94B49521, 0x66DF1622, 0x38CC2A06, 0xCAA7A905, 0xD9F75AF1, 0x2B9CD9F2, 0xFF56BD19, 0x0D3D3E1A, 0x1E6DCDEE, 0xEC064EED, 0xC38D26C4, 0x31E6A5C7, 0x22B65633, 0xD0DDD530, 0x0417B1DB, 0xF67C32D8, 0xE52CC12C, 0x1747422F, 0x49547E0B, 0xBB3FFD08, 0xA86F0EFC, 0x5A048DFF, 0x8ECEE914, 0x7CA56A17, 0x6FF599E3, 0x9D9E1AE0, 0xD3D3E1AB, 0x21B862A8, 0x32E8915C, 0xC083125F, 0x144976B4, 0xE622F5B7, 0xF5720643, 0x07198540, 0x590AB964, 0xAB613A67, 0xB831C993, 0x4A5A4A90, 0x9E902E7B, 0x6CFBAD78, 0x7FAB5E8C, 0x8DC0DD8F, 0xE330A81A, 0x115B2B19, 0x020BD8ED, 0xF0605BEE, 0x24AA3F05, 0xD6C1BC06, 0xC5914FF2, 0x37FACCF1, 0x69E9F0D5, 0x9B8273D6, 0x88D28022, 0x7AB90321, 0xAE7367CA, 0x5C18E4C9, 0x4F48173D, 0xBD23943E, 0xF36E6F75, 0x0105EC76, 0x12551F82, 0xE03E9C81, 0x34F4F86A, 0xC69F7B69, 0xD5CF889D, 0x27A40B9E, 0x79B737BA, 0x8BDCB4B9, 0x988C474D, 0x6AE7C44E, 0xBE2DA0A5, 0x4C4623A6, 0x5F16D052, 0xAD7D5351, 0xD76AA478]
table = string.digits + string.ascii_letters
maps = itertools.product(table, table, table, table)
flag4 = ""
for m in maps:
    key = "".join(m)
    v1 = 0xffffffff
    for v3 in key:
        v1 = (v1 >> 8) ^ dword_1400034B0[(ord(v3) ^ v1) & 0xff]
        v1 &= 0xffffffff
    if ~v1 & 0xffffffff == 0xCAFABCBC:
        flag4 = key
        break
# print(flag4)

后来想了想也可Z3

# Part2
import z3


dword_1400034B0 = [0x00000000, 0xF26B8303, 0xE13B70F7, 0x1350F3F4, 0xC79A971F, 0x35F1141C, 0x26A1E7E8, 0xD4CA64EB, 0x8AD958CF, 0x78B2DBCC, 0x6BE22838, 0x9989AB3B, 0x4D43CFD0, 0xBF284CD3, 0xAC78BF27, 0x5E133C24, 0x105EC76F, 0xE235446C, 0xF165B798, 0x030E349B, 0xD7C45070, 0x25AFD373, 0x36FF2087, 0xC494A384, 0x9A879FA0, 0x68EC1CA3, 0x7BBCEF57, 0x89D76C54, 0x5D1D08BF, 0xAF768BBC, 0xBC267848, 0x4E4DFB4B, 0x20BD8EDE, 0xD2D60DDD, 0xC186FE29, 0x33ED7D2A, 0xE72719C1, 0x154C9AC2, 0x061C6936, 0xF477EA35, 0xAA64D611, 0x580F5512, 0x4B5FA6E6, 0xB93425E5, 0x6DFE410E, 0x9F95C20D, 0x8CC531F9, 0x7EAEB2FA, 0x30E349B1, 0xC288CAB2, 0xD1D83946, 0x23B3BA45, 0xF779DEAE, 0x05125DAD, 0x1642AE59, 0xE4292D5A, 0xBA3A117E, 0x4851927D, 0x5B016189, 0xA96AE28A, 0x7DA08661, 0x8FCB0562, 0x9C9BF696, 0x6EF07595, 0x417B1DBC, 0xB3109EBF, 0xA0406D4B, 0x522BEE48, 0x86E18AA3, 0x748A09A0, 0x67DAFA54, 0x95B17957, 0xCBA24573, 0x39C9C670, 0x2A993584, 0xD8F2B687, 0x0C38D26C, 0xFE53516F, 0xED03A29B, 0x1F682198, 0x5125DAD3, 0xA34E59D0, 0xB01EAA24, 0x42752927, 0x96BF4DCC, 0x64D4CECF, 0x77843D3B, 0x85EFBE38, 0xDBFC821C, 0x2997011F, 0x3AC7F2EB, 0xC8AC71E8, 0x1C661503, 0xEE0D9600, 0xFD5D65F4, 0x0F36E6F7, 0x61C69362, 0x93AD1061, 0x80FDE395, 0x72966096, 0xA65C047D, 0x5437877E, 0x4767748A, 0xB50CF789, 0xEB1FCBAD, 0x197448AE, 0x0A24BB5A, 0xF84F3859, 0x2C855CB2, 0xDEEEDFB1, 0xCDBE2C45, 0x3FD5AF46, 0x7198540D, 0x83F3D70E, 0x90A324FA, 0x62C8A7F9, 0xB602C312, 0x44694011, 0x5739B3E5, 0xA55230E6, 0xFB410CC2, 0x092A8FC1, 0x1A7A7C35, 0xE811FF36, 0x3CDB9BDD, 0xCEB018DE, 0xDDE0EB2A, 0x2F8B6829, 0x82F63B78, 0x709DB87B, 0x63CD4B8F, 0x91A6C88C, 0x456CAC67, 0xB7072F64, 0xA457DC90, 0x563C5F93, 0x082F63B7, 0xFA44E0B4, 0xE9141340, 0x1B7F9043, 0xCFB5F4A8, 0x3DDE77AB, 0x2E8E845F, 0xDCE5075C, 0x92A8FC17, 0x60C37F14, 0x73938CE0, 0x81F80FE3, 0x55326B08, 0xA759E80B, 0xB4091BFF, 0x466298FC, 0x1871A4D8, 0xEA1A27DB, 0xF94AD42F, 0x0B21572C, 0xDFEB33C7, 0x2D80B0C4, 0x3ED04330, 0xCCBBC033, 0xA24BB5A6, 0x502036A5, 0x4370C551, 0xB11B4652, 0x65D122B9, 0x97BAA1BA, 0x84EA524E, 0x7681D14D, 0x2892ED69, 0xDAF96E6A, 0xC9A99D9E, 0x3BC21E9D, 0xEF087A76, 0x1D63F975, 0x0E330A81, 0xFC588982, 0xB21572C9, 0x407EF1CA, 0x532E023E, 0xA145813D, 0x758FE5D6, 0x87E466D5, 0x94B49521, 0x66DF1622, 0x38CC2A06, 0xCAA7A905, 0xD9F75AF1, 0x2B9CD9F2, 0xFF56BD19, 0x0D3D3E1A, 0x1E6DCDEE, 0xEC064EED, 0xC38D26C4, 0x31E6A5C7, 0x22B65633, 0xD0DDD530, 0x0417B1DB, 0xF67C32D8, 0xE52CC12C, 0x1747422F, 0x49547E0B, 0xBB3FFD08, 0xA86F0EFC, 0x5A048DFF, 0x8ECEE914, 0x7CA56A17, 0x6FF599E3, 0x9D9E1AE0, 0xD3D3E1AB, 0x21B862A8, 0x32E8915C, 0xC083125F, 0x144976B4, 0xE622F5B7, 0xF5720643, 0x07198540, 0x590AB964, 0xAB613A67, 0xB831C993, 0x4A5A4A90, 0x9E902E7B, 0x6CFBAD78, 0x7FAB5E8C, 0x8DC0DD8F, 0xE330A81A, 0x115B2B19, 0x020BD8ED, 0xF0605BEE, 0x24AA3F05, 0xD6C1BC06, 0xC5914FF2, 0x37FACCF1, 0x69E9F0D5, 0x9B8273D6, 0x88D28022, 0x7AB90321, 0xAE7367CA, 0x5C18E4C9, 0x4F48173D, 0xBD23943E, 0xF36E6F75, 0x0105EC76, 0x12551F82, 0xE03E9C81, 0x34F4F86A, 0xC69F7B69, 0xD5CF889D, 0x27A40B9E, 0x79B737BA, 0x8BDCB4B9, 0x988C474D, 0x6AE7C44E, 0xBE2DA0A5, 0x4C4623A6, 0x5F16D052, 0xAD7D5351, 0xD76AA478]
sol = z3.Solver()
flag4 = [z3.BitVec(f"a{str(i)}", 8) for i in range(4)]
mapping = z3.Array("mapping", z3.BitVecSort(32), z3.BitVecSort(32))
for i in range(len(dword_1400034B0)):
    sol.add(mapping[i] == dword_1400034B0[i])

# 太慢
# for i, v in enumerate(dword_1400034B0):
#     mapping = z3.Store(mapping, i, v)

v1 = z3.BitVecVal(0xFFFFFFFF, 32)

for i in range(4):
    idx = (z3.ZeroExt(24, flag4[i]) ^ v1) & 0xff
    v1 = z3.LShR(v1, 8) ^ mapping[idx]

sol.add(~v1 & 0xffffffff == 0xCAFABCBC)

assert sol.check() == z3.sat
ros = sol.model()
flag4 = "".join([chr(ros.eval(j).as_long()) for j in flag4])
print(flag4)

而后又是模拟执行

与之前相同,其架构为ARM X86

https://cdn.shi1011.cn/2021/11/3be7a010b28b3b83a65891e89e4cc852.png?imageMogr2/format/webp/interlace/0/quality/90|watermark/2/text/wqlNYXMwbg/font/bXN5aGJkLnR0Zg/fontsize/14/fill/IzMzMzMzMw/dissolve/80/gravity/southeast/dx/5/dy/5

dump后放进IDA进行Rebase

https://cdn.shi1011.cn/2021/11/604d11ce14cf68d9e6024f857696fbdc.png?imageMogr2/format/webp/interlace/0/quality/90|watermark/2/text/wqlNYXMwbg/font/bXN5aGJkLnR0Zg/fontsize/14/fill/IzMzMzMzMw/dissolve/80/gravity/southeast/dx/5/dy/5

这里可以创建多个节区,方便IDA分析

C转换为汇编后,选中所有代码,P创建函数

https://cdn.shi1011.cn/2021/11/5209f7e8b3fe80b6e58adc908edd5d2f.png?imageMogr2/format/webp/interlace/0/quality/90|watermark/2/text/wqlNYXMwbg/font/bXN5aGJkLnR0Zg/fontsize/14/fill/IzMzMzMzMw/dissolve/80/gravity/southeast/dx/5/dy/5

这里的135204指向的是最后16个字节首地址

在对应地址创建变量char raw4[0x1d],按U和之前的办法重建函数

https://cdn.shi1011.cn/2021/11/60a8c4f80b9a5a7874cd8a76e5547b0a.png?imageMogr2/format/webp/interlace/0/quality/90|watermark/2/text/wqlNYXMwbg/font/bXN5aGJkLnR0Zg/fontsize/14/fill/IzMzMzMzMw/dissolve/80/gravity/southeast/dx/5/dy/5

分析hook回调sub_140001680

https://cdn.shi1011.cn/2021/11/8328a643d41826bc7321cff03d65f114.png?imageMogr2/format/webp/interlace/0/quality/90|watermark/2/text/wqlNYXMwbg/font/bXN5aGJkLnR0Zg/fontsize/14/fill/IzMzMzMzMw/dissolve/80/gravity/southeast/dx/5/dy/5

显而易见是动态修改了密文

https://cdn.shi1011.cn/2021/11/d513c509edf1b912e6ab1254c55213f5.png?imageMogr2/format/webp/interlace/0/quality/90|watermark/2/text/wqlNYXMwbg/font/bXN5aGJkLnR0Zg/fontsize/14/fill/IzMzMzMzMw/dissolve/80/gravity/southeast/dx/5/dy/5

当代码运行完毕,取出R0寄存器的值进行最终的判断

# Part3
import z3

key3 = list(b")8FP>6^B=G6@>X*P<G=B)1  ")
key3[0] += 15
key3[1] ^= 0x6f
key3[2] -= 12
key3[3] ^= 0x12
key3[4] -= 5
key3[5] += 33
key3[6] -= 12
key3[7] ^= 0xd
key3[8] -= 3
key3[9] += 15
key3[10] ^= 0x68
key3[11] ^= 0xa
key3[12] -= 5
key3[13] -= 33
key3[14] += 48
key3[15] ^= 0x18
key3[16] += 2
key3[17] -= 16
key3[18] ^= 0x1b
key3[19] += 6
key3[20] ^= 0x13

flag5 = ""
for i in range(0, len(key3), 4):

    sol = z3.Solver()

    if i < 5 * 4:
        raw4 = [z3.BitVec(f"a{str(j)}", 8) for j in range(3)]
        sol.add(((raw4[0] >> 2) + 33) == key3[i])
        sol.add(((16 * raw4[0]) & 0x30 | (raw4[1] >> 4) & 0xff) + 33 == key3[i + 1])
        sol.add(((4 * raw4[1]) & 0x3C | (raw4[2] >> 6) & 0xff) + 33 == key3[i + 2])
        sol.add((raw4[2] & 0x3F) + 33 == key3[i + 3])
    else:
        raw4 = [z3.BitVec(f"a{str(j)}", 8) for j in range(1)]
        sol.add((raw4[0] >> 2) + 33 == key3[i])
        sol.add(((16 * raw4[0]) & 0x30) + 33 == key3[i+1])


    assert sol.check() == z3.sat
    ros = sol.model()

    flag5 += "".join([chr(ros.eval(j).as_long()) for j in raw4 if isinstance(j, z3.BitVecRef)])
# print(flag5)

最终拼接,md5

print(flag1 + flag2 + flag3 + flag4 + flag5)

Crypto

EasyCrypto

给了ELF

没去符号……函数名就能知道是AES,魔改过

简单分析一下GenIV

生成的两个其实都指向同一个地址,所以key和iv相同

后面也能看到限制了输入文本不能包含admin

if ( std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::find(v54, "admin", 0LL) != -1
          || (std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::length(v54) & 0xF) != 0 )
        {
          v11 = std::operator<<<std::char_traits<char>>(&std::cout, "no no no");
          std::ostream::operator<<(v11, &std::endl<char,std::char_traits<char>>);
          exit(0);
        }

与后文解密后明文开头adminadmin产生互斥

v38 = (unsigned int)std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::compare(
                            v54,
                            "adminadmin") == 0;
      std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::~basic_string(v54);
      if ( v38 )
      {
        v39 = std::operator<<<char>(&std::cout, &flag[abi:cxx11]);
        std::ostream::operator<<(v39, &std::endl<char,std::char_traits<char>>);
      }

然而程序包含了encodedecode,由于是对称加密,直接nc拿到回显的AES IV

IDA调试,patch下iv

# -*- coding:utf-8 -*-
"""
@Author: Mas0n
@File: ida_quick_script.py
@Time: 2021-08-17 10:41
@Desc: It's all about getting better.
"""

addr = 0x055CAA9008300  # patch address

test = "b949b7688a7ab3bc26862d161312831e"  # patch hex data

ps = [i for i in b''.fromhex(test)]
for i, v in enumerate(ps):
    ida_bytes.patch_qword(addr+i * 8, v)

选择 1. register,输入文本adminadmin然后随机字符填充至16位(原因后面讲)

直接跳过包含admin判断,执行encode(__int64 key, __int64 iv, __int64 cipher, int strlen)

进入正常流程

std::operator<<<std::char_traits<char>>(&std::cout, "Here is your token: ");
        for ( k = 0; ; ++k )
        {
          v19 = k;
          if ( v19 >= std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::length(v54) ) // 输入文本的长度
            break;
          v20 = std::ostream::operator<<(&std::cout, std::hex);
          v21 = std::setfill<char>(48LL);
          v22 = std::operator<<<char,std::char_traits<char>>(v20, v21);
          v23 = std::setw(2);
          v24 = std::operator<<<char,std::char_traits<char>>(v22, v23);
          v25 = std::bitset<8ul>::to_ulong(8LL * k + v51);
          std::ostream::operator<<(v24, v25);
        }
        std::ostream::operator<<(&std::cout, &std::endl<char,std::char_traits<char>>);
        std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::~basic_string(v54);

从中可以知道加密后的密文长度由输入文本长度决定,因此填充到16位,或者硬改循环16次

https://cdn.shi1011.cn/2021/10/d746452031963dc3525d73bb758c00c4.png?imageMogr2/format/webp/interlace/0/quality/90|watermark/2/text/wqlNYXMwbg/font/bXN5aGJkLnR0Zg/fontsize/14/fill/IzMzMzMzMw/dissolve/80/gravity/southeast/dx/5/dy/5
https://cdn.shi1011.cn/2021/10/9fceb3b139efc6ff3b6a805344344bba.png?imageMogr2/format/webp/interlace/0/quality/90|watermark/2/text/wqlNYXMwbg/font/bXN5aGJkLnR0Zg/fontsize/14/fill/IzMzMzMzMw/dissolve/80/gravity/southeast/dx/5/dy/5

login提交

https://cdn.shi1011.cn/2021/10/7307e7f298b9d9bc6b2a51839fdb067a.png?imageMogr2/format/webp/interlace/0/quality/90|watermark/2/text/wqlNYXMwbg/font/bXN5aGJkLnR0Zg/fontsize/14/fill/IzMzMzMzMw/dissolve/80/gravity/southeast/dx/5/dy/5

Easy Railfence

变种的W型栅栏密码,多了个偏移

标准W型栅栏脚本改下就行

# -*- coding:utf-8 -*-
"""
@Author: Mas0n
@File: zjctf2.py
@Time: 2021-10-23 13:42
@Desc: It's all about getting better.
"""

import hashlib
import math

def npmyG(string, n):
    '''将字符排列成w型'''
    array = [['.'] * len(string) for i in range(n)]
    row = 0
    upflag = False
    for col in range(len(string)):
        array[row][col] = string[col]
        if row == n - 1:
            upflag = True
        if row == 0:
            upflag = False
        if upflag:
            row -= 1
        else:
            row += 1
    return array

def decode(string, n):
    '''解密'''
    array = npmyG(string, n)
    sub = 0
    for row in range(n):
        for col in range(len(string)):
            if array[row][col] != '.':
                array[row][col] = string[sub]
                sub += 1
    msg = []
    for col in range(len(string)):
        for row in range(n):
            if array[row][col] != '.':
                msg.append(array[row][col])
    return msg


def getArrs(c, rails, offset):
    c = '$' * offset + c
    length = len(c)
    result = {x: "" for x in range(rails)}
    for a in range(length):
        width = rails * 2 - 2
        num = a % width
        # {0: '$g941d6', 1: '$a{1ffd4de049', 2: '$la0ec5daad1f}', 3: 'f44db28'}
        if (num < rails):
            result.update({num: result[num] + c[a]})
        else:
            ll = 2 * rails - 2 - num
            result.update({ll: result[ll] + c[a]})
    # d = ""
    arr = []
    for k in range(rails):
        # d = d + result[k]
        arr.append(len(result[k]))
    # print(d)
    # d = d.replace('$', '')
    # print(result)
    return arr

def encrypt(c,rails,offset):
    c = '$' * offset + c
    length = len(c)
    result = {x: "" for x in range(rails)}
    for a in range(length):
        width = rails * 2 - 2
        num = a % width
        if (num < rails):
            result.update({num: result[num] + c[a]})
        else:
            ll = 2 * rails - 2 - num
            result.update({ll: result[ll] + c[a]})
    d = ""
    for k in range(rails):
        d = d + result[k]
    d = d.replace('$','')

    return d

def decrypt(c, rails, offset, decarr):
    length = len(c) + offset
    rawWidth = math.ceil(len(c) / rails)

    result = {x: "" for x in range(rails)}
    for a in range(offset):
        num = a % rails
        result.update({num: result[num] + '$'})
    arrs = []
    for a in range(rails):
        arrs.append(c[a * rawWidth: (a + 1) * rawWidth])

    # print(arrs)
    # print(result)
    data = "".join(arrs)

    index = 0
    endArr = []
    for k in result:
        tmp = decarr[k] - len(result[k])
        endArr.append(result[k] + data[index:index+tmp])
        index += tmp

    return "".join(endArr)


m, Rail, Offset = "flag{a4019fe4cf4d5dd41dabaed0d21469019fe4cf4d51dabaed0d21469f8}", 13, 5

test = encrypt(m, Rail, Offset)

decarrs = getArrs(m, Rail, Offset)
# print(decrypt(test, Rail, Offset, decarrs))

d = "reetdrvhns0eutbftafmeon}linnd=a1cOh!gcedos{neuwkYav0irOceytounw"

decrypt(d, Rail, Offset, decarrs)

for rail in range(2, 20):
    for offset in range(20):
        decarrs = getArrs(m, rail, offset)
        string = decrypt(d, rail, offset, decarrs)
        flag = ''.join(decode(string, rail)).replace("$", "")
        if flag.startswith("flag") and flag.endswith("}"):
            print(flag)
            flag = hashlib.md5(flag.encode()).hexdigest()
            print(flag)
            exit()
本文链接:https://blog.shi1011.cn/ctf/1700
本文采用 CC BY-NC-SA 4.0 Unported 协议进行许可
# #
首页      ctf      第四届浙江省大学生网络与信息安全竞赛(预赛)

Mas0n

文章作者

回复 Scr1pt 取消回复

textsms
account_circle
email

  • Scr1pt

    师傅,unicorn第一部分的那个mips32的汇编代码怎么看出来是除法的啊

    2年前 回复

翻车鱼

第四届浙江省大学生网络与信息安全竞赛(预赛)
今年省赛比去年难度大点,用的安恒的平台,前2小时原地坐牢。体验了一波flag交不上,附件下不了。后期有了些体验感……有个吐槽点就是不能联网……unicorn的题一出我直接傻眼,本地没有头文件…
扫描二维码继续阅读
2021-10-23