mcfx's blog

题解、Writeup、游记和碎碎念

“第五空间”智能安全大赛 Writeup

Misc

loop

Zip 套 tar 套 zip 套 tar……写个脚本依次解开就好了

import zipfile
import tarfile
import os

def work(fi,fo):
    f=zipfile.ZipFile(fi)
    f1=f.open('tarfile')
    f2=tarfile.TarFile(None,'r',f1)
    f2.extract('zipfile','.')
    os.rename('zipfile',fo)

lst='file'
for i in range(1000):
    print(i)
    cur='tmp/file'+str(i)
    work(lst,cur)
    lst=cur

Crypto

rosb

rsa ,给了两组 (e,c)(e,c),且两个 ee 互质。可以对两个 eegcd\gcd,并在此过程中维护对应的 cc

from Crypto.Util.number import long_to_bytes,bytes_to_long,getPrime
from gmpy2 import *

n=0xa1d4d377001f1b8d5b2740514ce699b49dc8a02f12df9a960e80e2a6ee13b7a97d9f508721e3dd7a6842c24ab25ab87d1132358de7c6c4cee3fb3ec9b7fd873626bd0251d16912de1f0f1a2bba52b082339113ad1a262121db31db9ee1bf9f26023182acce8f84612bfeb075803cf610f27b7b16147f7d29cc3fd463df7ea31ca860d59aae5506479c76206603de54044e7b778e21082c4c4da795d39dc2b9c0589e577a773133c89fa8e3a4bd047b8e7d6da0d9a0d8a3c1a3607ce983deb350e1c649725cccb0e9d756fc3107dd4352aa18c45a65bab7772a4c5aef7020a1e67e6085cc125d9fc042d96489a08d885f448ece8f7f254067dfff0c4e72a63557
e1=0xf4c1158f
c1=0x2f6546062ff19fe6a3155d76ef90410a3cbc07fef5dff8d3d5964174dfcaf9daa003967a29c516657044e87c1cbbf2dba2e158452ca8b7adba5e635915d2925ac4f76312feb3b0c85c3b8722c0e4aedeaec2f2037cc5f676f99b7260c3f83ffbaba86cda0f6a9cd4c70b37296e8f36c3ceaae15b5bf0b290119592ff03427b80055f08c394e5aa6c45bd634c80c59a9f70a92dc70eebec15d4a5e256bf78775e0d3d14f3a0103d9ad8ea6257a0384091f14da59e52581ba2e8ad3adb9747435e9283e8064de21ac41ab2c7b161a3c072b7841d4a594a8b348a923d4cc39f02e05ce95a69c7500c29f6bb415c11e4e0cdb410d0ec2644d6243db38e893c8a3707
e2=0xf493f7d1
c2=0xd32dfad68d790022758d155f2d8bf46bb762ae5cc17281f2f3a8794575ec684819690b22106c1cdaea06abaf7d0dbf841ebd152be51528338d1da8a78f666e0da85367ee8c1e6addbf590fc15f1b2182972dcbe4bbe8ad359b7d15febd5597f5a87fa4c6c51ac4021af60aeb726a3dc7689daed70144db57d1913a4dc29a2b2ec34c99c507d0856d6bf5d5d01ee514d47c7477a7fb8a6747337e7caf2d6537183c20e14c7b79380d9f7bcd7cda9e3bfb00c2b57822663c9a5a24927bceec316c8ffc59ab3bfc19f364033da038a4fb3ecef3b4cb299f4b600f76b8a518b25b576f745412fe53d229e77e68380397eee6ffbc36f6cc734815cd4065dc73dcbcb

def gcd(a,b,x,y):
    if b==0:
        assert y==1
        return a,x
    v=a//b
    return gcd(b,a%b,y,x*invert(pow(y,v,n),n)%n)

res=gcd(e1,e2,c1,c2)
print(long_to_bytes(res[1]))

Reverse

nop

程序先检查 getenv("_")argv[0] 是否相等,相等就退出;后来又检查 getpidgetppid 是否相等,不等就退出,这导致他不管有没有被调试,都跑不起来。

patch 掉这些之后,设输入为 xx,程序会把 x+0xcccccccfx+\text{0xcccccccf}x+0xcccccccf+1x+\text{0xcccccccf}+1 这两个位置改成 0x90,即 nop。为了使程序输出 right,应该修改 0x8048765,则输入为 993507990。

ManageCode

ida 用 .net 模式打开,可以看到 main 函数中调用了 MainLogic2Check 去检查 flag,而这是个 native 的函数。

用普通 exe 的模式打开,调试,输入完 flag 后,给 flag 下读写断点,再单步跟一会,可以找到 MainLogic2Check 函数。

而调用到的三个函数,第二个是 unhex,第一个和第三个都是 .net 函数。.net 函数需要单步跟一段时间,然后在堆里找到实际执行的代码。第一个函数是 check 格式用的,而第三个是一些巨大的方程。方程的伪代码用 z3 解一下就可以得到 flag。

from z3 import *
from binascii import hexlify

a1o=[BitVec('s'+str(i),32) for i in range(16)]
a1=[a1o[i] & 255 for i in range(16)]
solver=Solver()

v1 = 1;
v2 = a1[0];
v3 = a1[1];
v35 = v2;
v36 = v3;
solver.add( 215652 * v2 == 4744344 )
v1 = 0;
v4 = 188182 * v2;
v5 = a1[2];
v34 = v5;
solver.add ( v4 + 364470 * v3 == 72295894 )
v1 = 0;
v6 = 509425 * v3;
v7 = a1[3];
v8 = a1[4];
v33 = v7;
v32 = v8;
solver.add ( 85288 * v5 + v6 - 115680 * v35 == 94252699 )
v1 = 0;
solver.add ( 321876 * v35 + 234928 * v36 + 505832 * v5 - 519855 * v7 == 18529384 )
v1 = 0;
v9 = 379791 * v7;
v10 = v1;
solver.add ( -32132 * v35 - 176612 * v8 - 453270 * v36 - v9 - 83868 * v5 == -122836270 )
v10 = 0;
v11 = a1[5];
v31 = v11;
solver.add ( -514891 * v36 - 515329 * v34 + 195828 * v8 + 299453 * v35 + 300977 * v11 + 278760 * v33 == -51639554 )
v10 = 0;
v12 = a1[6];
v30 = v12;
v13 = v10;
solver.add ( 163500 * v34 + 67778 * v12 + 138714 * v33 + 309624 * v36 + -46340 * v11 - 213112 * v32 - 259627 * v35 == 64903260 )
v13 = 0;
v14 = v13;
v15 = a1[7];
v27 = v15;
solver.add ( 380043 * v12
+ 229336 * v35
+ 32838 * v36
+ 397667 * v15
+ 238886 * v32
- 473199 * v34
- 37539 * v31
- 128671 * v33 == 81178913 )
v14 = 0;
v16 = a1[8];
v29 = v16;
solver.add ( 155235 * v35
+ 348728 * v30
+ 289914 * v34
+ -516637 * v15
- 264676 * v32
+ 67047 * v16
+ 11049 * v31
+ 113723 * v33
- 443726 * v36 == -76487387 )
v14 = 0;
v17 = a1[9];
v28 = v17;
solver.add ( 87093 * v27
+ 349845 * v17
+ 494472 * v32
+ 35867 * v16
- 505677 * v35
+ -257608 * v31
- 129391 * v33
- 63270 * v36
- 319671 * v30
- 513747 * v34 == -108319 )
v14 = 0;
v18 = a1[10];
v26 = v18;
solver.add ( -111003 * v18
- 188371 * v31
- 279658 * v30
+ 449035 * v29
+ 456975 * v33
+ 221803 * v17
+ -427385 * v35
- 496409 * v34
+ 164625 * v27
+ 102646 * v32
+ 367827 * v36 == 121365140 )
v14 = 0;
v19 = a1[11];
v25 = v19;
solver.add ( 283626 * v34
+ 20322 * v27
+ 511450 * v29
+ -419684 * v32
- 288095 * v30
+ 458175 * v31
+ 325113 * v28
+ 366156 * v36
+ 180175 * v35
- 201966 * v18
- 419075 * v33
- 370704 * v19 == 37790278 )
v14 = 0;
v20 = a1[12];
v24 = v20;
solver.add ( 48958 * v27
+ 139571 * v20
+ 510622 * v30
+ 289232 * v34
+ 168693 * v28
+ 466762 * v29
+ 50528 * v36
- 16029 * v35
+ -127198 * v26
- 368880 * v32
- 512009 * v19
- 352441 * v31
- 382522 * v33 == -11101221 )
v14 = 0;
v21 = a1[13];
solver.add ( 109811 * v25
+ 504571 * v20
+ -62723 * v36
- 252863 * v26
+ 313963 * v21
+ 389118 * v29
+ 429789 * v34
+ 457768 * v27
+ 139696 * v31
- 398963 * v28
- 171152 * v30
- 500169 * v32
- 235951 * v33
- 429574 * v35 == 48202710 )
v14 = 0;
v22 = a1[14];
solver.add ( 306824 * v28
+ 243694 * v29
+ 302256 * v21
+ 349714 * v27
+ 356687 * v35
+ 21624 * v26
+ 326568 * v33
+ -132020 * v32
- 188851 * v25
+ 483573 * v24
+ -433096 * v30
- 176223 * v34
- 251583 * v22
- 127299 * v31
- 249177 * v36 == 97596703 )
v14 = 0;
result = v14;
solver.add ( -280530 * v32
- 83572 * v26
- 113178 * v27
- 281771 * v35
+ 19871 * v28
+ -193997 * v33
- 520319 * v30
+ 154211 * v24
+ 233576 * v36
+ -5255 * v34
- 482259 * v22
+ 60875 * v21
+ 364007 * v29
+ 468908 * v31
+ 291455 * a1[15]
- 164806 * v25 == -6084871 )
print(solver.check())
m=solver.model()
res=[]
for i in a1o:
    res.append(m[i].as_long())
print(hexlify(bytes(res)))

rev

程序的主要部分,每执行几条指令就会 ret。

把执行到的指令拿出来,可以看出用来控制执行流的方法是把 rsp 加上数。改成 jmp 之后,重新汇编,再丢进 ida,可以发现一处判断 flag 格式是 ctf{16 个字符} 的东西。而其他和 flag 相关的部分就很难看出了。同时可以看出,执行流也被 ebp 控制,但是和 flag 关系不大。

于是考虑用 angr 求 flag,结果求出来只要是满足格式的都可以通过。这时才发现 bin 更新了。把新的文件下下来,脚本改一下文件名,就出了。

import angr
import claripy
import logging

proj = angr.Project('./rev_v2')
flag = claripy.BVS('flag',21*8)
state = proj.factory.entry_state(args=['./rev',flag])
simgr = proj.factory.simgr(state)

simgr.explore(find=0x400481)
print(simgr.found)
print(simgr.found[0].solver.eval(flag))
print(simgr.found[0].solver.eval(flag,cast_to=bytes))

日期: 2020-06-29

标签: CTF Writeup

这是一篇旧文,原始文章及评论可在 https://oldblog.mcfx.us/archives/285/ 查看。