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$ 互质。可以对两个 $e$ 求 $\gcd$,并在此过程中维护对应的 $c$。

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 掉这些之后,设输入为 $x$,程序会把 $x+\text{0xcccccccf}$ 和 $x+\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))