CrackRTF

拿到一个EXE,无壳,运行看看

Snipaste_2021-07-27_09-38-34

拖进IDA,进入主函数

一共需要输入两次密码,长度都为6

  • 第一个密码经历过atoi(参数 str 所指向的字符串转换为一个整数,类型为 int 型, 如果字符串是数字,则转换为相应int型数据,如果为字母则为0)函数,并且小于100000,再拼接@DBApp,传进sub_40100A函数,在于某一个字符串比较
  • 第二个密码并不是纯数字,将输入的字符串与第一个密码(拼接后)再次拼接,再传进sub_401019函数,再与一串字符比较,相同再调用sub_40100F函数

先看第一个密码加密的函数sub_40100A

百度得知是一个WinAPICryptCreateHash是一个可以进行很多Hash算法的函数,0x8004是进行SHA1算法,得知字符串是6位数字可以爆破

BOOL CryptCreateHash(
  HCRYPTPROV hProv,
  ALG_ID     Algid,  // 加密类型
  HCRYPTKEY  hKey,
  DWORD      dwFlags,
  HCRYPTHASH *phHash
);

ALG_ID

from hashlib import sha1

sha_1 = "6E32D0943418C2C33385BC35A1470250DD8923A9"
for i in range(100000, 10000000):
    pass1 = str(i) + "@DBApp"
    s1 = sha1()
    s1.update(pass1.encode())
    res = s1.hexdigest().upper()
    if res == sha_1:
        print(pass1)
        break
# 解出第一个密码为 `123321`

第二个密码,不肯定是全数字,加密算法是0x8003,查询后得知是MD5,那爆破成功的机率就很微小了,继续往下看,进入sub_40100F函数

通过findResourceA找到名为AAA的资源,通过和第二次输入的密码拼接之后的字符串做异或,写入dbapp.rtf

unsigned int __cdecl sub_401005(LPCSTR lpString, int a2, int a3)
{
  unsigned int result; // eax
  unsigned int i; // [esp+4Ch] [ebp-Ch]
  unsigned int v5; // [esp+54h] [ebp-4h]
  v5 = lstrlenA(lpString);
  for ( i = 0; ; ++i ){
    result = i;
    if ( i >= a3 )
      break;
    *(_BYTE *)(i + a2) ^= lpString[i % v5];
  }
  return result;
}

也就是pass2 ^ [AAA] = rtf,参考writeup,使用ResourceHacker找到AAA资源,在和rtf的前六个字节异或即可得到我们需要输入的密码

AAA = [0x05, 0x7D, 0x41, 0x15, 0x26, 0x01]
rtf_header = r'{\rtf1'

p2 = ""
for i, c in enumerate(rtf_header):
    tmp = ord(c)
    p2 += chr(tmp ^ AAA[i])
print(p2)
# ~!3a@0

得到第二次密码:~!3a@0

运行程序,依次输入两次密码就会生成一个rtf文件,内容为flag

Snipaste_2021-07-27_11-50-35

Flag{N0_M0re_Free_Bugs}

[2019红帽杯]easyRE

Snipaste_2021-07-27_11-54-24

关键函数在主函数下一个函数

[ACTF新生赛2020]rome

Snipaste_2021-07-27_18-56-31

无壳,拖进IDA

Snipaste_2021-07-27_19-02-34

s = "Qsw3sj_lz4_Ujw@l"
flag = ""

for i in range(len(s)):
    c = ord(s[i])
    if 64 < c <= 90:
        c -= 65
        c -= 14
        c %= 26
        flag += chr(65+c)
    elif 96 < c <= 122:
        c -= 97
        c -= 18
        c %= 26
        flag += chr(97+c)
    else:
        flag += s[i]
print("ACTF{"+flag+"}")


s = "Qsw3sj_lz4_Ujw@l"
flag = ""

for i in range(len(s)):
    c = ord(s[i])
    if 64 < c <= 90:
        c -= 65
        c += 51
        c += 26
        if c > 90:
            c -= 26
        flag += chr(c)
    elif 96 < c <= 122:
        c -= 97
        c += 79
        c += 26
        if c > 122:
            c -= 26
        flag += chr(c)
    else:
        flag += s[i]
print("ACTF{"+flag+"}")

s = "Qsw3sj_lz4_Ujw@l"
flag = ""

for i in range(len(s)):
    c = ord(s[i])
    if 64 < c <= 90:
        c -= 65
        c += 12
        c %= 26
        flag += chr(65+c)
    elif 96 < c <= 122:
        c -= 97
        c += 8
        c %= 26
        flag += chr(97+c)
    else:
        flag += s[i]
print("ACTF{"+flag+"}")

# ACTF{Cae3ar_th4_Gre@t}

应该还有一种解法,通过动态调试,因为知道是凯撒加密,对称加密,将密文输入,在比较处下断点,应该就可知道明文是什么

但是得到却是Eko3kb_dr4_Ibo@d,方向错误?,得到的是以下运行的结果

s = "Qsw3sj_lz4_Ujw@l"
flag = ""

for i in range(len(s)):
    c = ord(s[i])
    if 64 < c <= 90:
        c -= 65
        c -= 12
        c %= 26
        flag += chr(65+c)
    elif 96 < c <= 122:
        c -= 97
        c -= 8
        c %= 26
        flag += chr(97+c)
    else:
        flag += s[i]
print("ACTF{"+flag+"}")

[FlareOn4]login

得到一个HTML

document.getElementById("prompt").onclick = function () {
    var flag = document.getElementById("flag").value;
    var rotFlag = flag.replace(/[a-zA-Z]/g, function(c){
        return String.fromCharCode((c <= "Z" ? 90 : 122) >= (c = c.charCodeAt(0) + 13) ? c : c - 26);});
    if ("PyvragFvqrYbtvafNerRnfl@syner-ba.pbz" == rotFlag) {
        alert("Correct flag!");
    } else {
        alert("Incorrect flag, rot again");
    }
    alert("flag{"+rotFlag+"}");  // 新增
}

是一个rot位移密码,rot密码其实可以看作是凯撒密码的一种变式,本质都是移位运算

只需新增一条alert,输入PyvragFvqrYbtvafNerRnfl@syner-ba.pbz即可

Snipaste_2021-07-27_19-27-44

flag{ClientSideLoginsAreEasy@flare-on.com}

[GUET-CTF2019]re

Snipaste_2021-07-27_19-37-32

UPX加固

拖进IDA,找到主函数

复制代码,正则提取数字,这里需要注意顺序,特别是a1[17]a1[16],并且少了第一个字符(在下标为6)第七个字符

b = [166163712, 731332800, 357245568, 1074393000, 489211344, 518971936, 406741500, 294236496, 177305856, 650683500,
     298351053, 386348487, 438258597, 249527520, 445362764, 174988800, 981182160, 493042704, 257493600, 767478780,
     312840624, 1404511500, 316139670, 619005024, 372641472, 373693320, 498266640, 452465676, 208422720, 515592000,
     719890500]

a = [1629056, 6771600, 3682944, 10431000, 3977328, 5138336, 7532250, 5551632, 3409728, 13013670, 6088797, 7884663,
     8944053, 5198490, 4544518, 3645600, 10115280, 9667504, 5364450, 13464540, 5488432, 14479500, 6451830, 6252576,
     7763364, 7327320, 8741520, 8871876, 4086720, 9374400, 5759124]

flag = ""
for i in range(len(a)):
    flag += chr(b[i] // a[i])
print(flag)
# flag{e65421110b0a3099a1c039337}
# flag{e165421110b0a3099a1c039337} 添加一个字符
# flag{e165421110ba03099a1c039337} 调换位置

[SUCTF2019]SignIn

无壳,拖进IDA

Snipaste_2021-07-27_20-50-19

Snipaste_2021-07-27_21-44-40

Snipaste_2021-07-27_21-46-50

// mpz_init_set_str的原型是:
int mpz_init_set_str (mpz_t rop, char *str, int base)
// 这三个参数分别是多精度整数变量,字符串,进制。

一个大数分解,已知n,e,以及密文v7

c = 0xad939ff59f6e70bcbfad406f2494993757eee98b91bc244184a377520d06fc35  # v7
n = 103461035900816914121390101299049044413950405173712170434161686539878160984549
e = 65537

先使用在线网站分解n

Snipaste_2021-07-27_21-49-41

得到qp

求到解d即可解密文

def rsa_moder(n):
    base = 2
    while base < n:
        if n % base == 0:
            return base, n // base
        base += 1

# 求欧拉函数f(n)
def rsa_get_euler(prime1, prime2):
    return (prime1 - 1) * (prime2 - 1)

# 求私钥
def rsa_get_key(e, euler):
    k = 1
    while True:
        if (((euler * k) + 1) % e) == 0:
            return (euler * k + 1) // e
        k += 1

# 根据n,e计算d(或根据n,d计算e)
def get_rsa_e_d(n, e=None, d=None):
    if e is None and d is None:
        return
    arg = e
    if arg is None:
        arg = d

    # primes = rsa_moder(n)
    # p = primes[0]
    # q = primes[1]
    # 在线分解n: http://www.factordb.com/
    p = 282164587459512124844245113950593348271
    q = 366669102002966856876605669837014229419

    d = rsa_get_key(arg, rsa_get_euler(p, q))
    return d

def orginal_algorithm(a, b, c):  # a^b%c
    ans = 1
    a = a % c  # 预处理,防止出现a比c大的情况
    for i in range(b):
        ans = (ans * a) % c
    return ans

def quick_algorithm(a, b, c):
    a = a % c
    ans = 1
    # 这里我们不需要考虑b<0,因为分数没有取模运算
    while b != 0:
        if b & 1:
            ans = (ans * a) % c
        b >>= 1
        a = (a * a) % c
    return ans

if __name__ == '__main__':
    """
    e: 公钥
    d: 私钥
    n = p*q
    n 大数分解使用在线网站: http://www.factordb.com/
    c = m^e mod n
    m = c^d mod n
    """
    c = 0xad939ff59f6e70bcbfad406f2494993757eee98b91bc244184a377520d06fc35 # 密文
    n = 103461035900816914121390101299049044413950405173712170434161686539878160984549
    p = 282164587459512124844245113950593348271
    q = 366669102002966856876605669837014229419
    e = 65537 # 公钥
    d = 91646299298871237857836940212608056141193465208586711901499120163393577626813 # 私钥
    d = get_rsa_e_d(n, e=e)
    print(d)
    # a = eval(input("底数:"))
    # b = eval(input("指数:"))
    # c = eval(input("模:"))
    # print("朴素算法结果%d" % (orginal_algorithm(a, b, c)))
    res = (quick_algorithm(c, d, n)) # 快速幂算法
    import binascii
    flag = binascii.unhexlify(hex(res)[2:]).decode()
    print(flag)
# suctf{Pwn_@_hundred_years}

Youngter-drive

检测到使用UPX加固,upx -d脱壳之后,拖进IDA

找到主函数

输入字符串,备份一份,创建两个线程,分别执行StartAddresssub_41119F函数

// StartAddress 线程一
void __stdcall StartAddress_0(int a1){
  while ( 1 ){
    WaitForSingleObject(hObject, 0xFFFFFFFF);
    if ( int_29 > -1 ){
      sub_41112C((int)input, int_29);
      --int_29;
      Sleep(100u);
    }
    ReleaseMutex(hObject);
  }
}
// sub_41112C 线程一
char *__cdecl sub_411940(int input, int int_29){
  char *result; // eax
  char v3; // [esp+D3h] [ebp-5h]

  v3 = *(_BYTE *)(int_29 + input);
  if ( (v3 < 97 || v3 > 122) && (v3 < 65 || v3 > 90) )
    exit(0);
  if ( v3 < 97 || v3 > 122 ){
    result = off_418000[0];  // off_418000 == QWERTYUIOPASDFGHJKLZXCVBNMqwertyuiopasdfghjklzxcvbnm
    *(_BYTE *)(int_29 + input) = off_418000[0][*(char *)(int_29 + input) - 38];
  }
  else{
    result = off_418000[0];
    *(_BYTE *)(int_29 + input) = off_418000[0][*(char *)(int_29 + input) - 96];
  }
  return result;
}
// sub_41119F 线程二
void __stdcall sub_411B10(int a1){
  while ( 1 ) {
    WaitForSingleObject(hObject, 0xFFFFFFFF);
    if ( int_29 > -1 )
    {
      Sleep(100u);
      --int_29;
    }
    ReleaseMutex(hObject);
  }
}

第一个线程就是将输入的字符判断到小写,然后根据给出了表进行交换,参考writeup才知道,还有一个线程负责将计数器-1,然后两个线程都有sleep(100ms)睡眠0.1秒,所以是每隔一个字符变化,至于是偶数还是奇数,都试试即可,因为后面判断加密后的字符串值判断了29位,但实际加密(变换了)0-29也就是30位

s = "TOiZiZtOrYaToUwPnToBsOaOapsyS"
map = "QWERTYUIOPASDFGHJKLZXCVBNMqwertyuiopasdfghjklzxcvbnm"

flag = ""
for i in range(len(s)):
    c_value = ord(s[i])
    if i % 2 == 0:
        flag += s[i]
    else:
        if 97 <= c_value <= 122:
            flag += chr(map.index(s[i]) + 38)
        else:
            flag += chr(map.index(s[i]) + 96)
print(f'flag{{{flag}}}')
# flag{ThisisthreadofwindowshahaIsES}
# 最后一个位实际可以任写,但是buuctf上是`E`

标签: RE

添加新评论