Wdream

Personal Website

The Sky's The Limit.


CTF

目录

NSSCTF{}

Png

对于一个PNG文件来说,其文件头总是由位固定的字节来描述的,

.PNG

89 50 4E 47 0D 0A 1A 0A

标准的PNG文件结构

`.PNG` `IHDR` `IDAT` `IEND`

strings 扫描

strings 1.png

实例结果

C:\Users\86182\Downloads>strings 1.png
IHDR
IDATx
?{ S
?{ S
?,W%
777}
(NuS
?[!S
1Kf&
&RKN
UL~b
_sIMX
'O$^R
(NuS
o<}y
3I      X
$$?k
1k#xU
WxbXK<
oUyM~A
3f-c
qFQ:)
OLm-9
(NUK<1
/Ev7}U
L<{5
_sIMX
W]fp
a-cb
!fILN
oUyM~A
/Ev7}U
y/>]
&#+P
od/d
I       _3
I       _3
I       _3
I       _3
IEND

没什么信息

010editor

或者其他十六进制查看/编辑器

搜索{ 或者}

image-20251226231914750

复制为十六进制文本

7B 20 53 01 FB 2F 53 7D
7B 20 53 01 FB 2F 53 7D
7B 35 8F 7D

pngcheck

pnggroup/pngcheck: Development of pngcheck 4

下载源码进入根目录cmd

cmake .
cmake --build . --config Release

使用实例

pngcheck 1.png
#OK: 1.png (213x213, 24-bit RGB, non-interlaced, static, 97.7%).

pngcheck -v 1.png
#File: 1.png (3097 bytes)
#  chunk IHDR at offset 0x0000c, length 13
#    213 x 213 image, 24-bit RGB, non-interlaced
#  chunk IDAT at offset 0x00025, length 3040
#    zlib: deflated, 32K window, superfast compression
#  chunk IEND at offset 0x00c11, length 0
#No errors detected in 1.png (3 chunks, 97.7% compression).

LSB隐写

zsteg

zsteg隐写分析工具

搜索text {}相关

image-20251226235430950

StegSolve

StegOnline

依次查看R G B为0时输出数据。

image-20251227000746140

文本txt

词频分析

WebShell

工具

蚁剑 源码

加载器 启动器启动后需要初始化,选择源码根目录。

Fiddler打开的时候会修改SSL证书导致picgo图片上传失败。

BurpSuite

直接连接

image-20251227121533702

提示需要POST wllm,暗示webshell连接密码为wllm

使用蚁剑连接

image-20251227121642239

手动查找flag,发现在顶层目录下。

image-20251227121731086

或者使用命令行搜索

find / -name "*flag*" 2>/dev/null

image-20251227122057580

cat打印

image-20251227122243895

Payload

<?php
highlight_file('index.php');
include("flag.php");
$id=$_POST['id'];
$json=json_decode($_GET['json'],true);
if ($id=="wllmNB"&&$json['x']=="wllm")
{echo $flag;}
?>

提示需要发送POST 和 GET,需要满足id为wllmNB 以及 json['x']=="wllm"

打开fiddler,选择composer组件

URL,默认http1.1

http://node7.anna.nssctf.cn:27699/index.php

添加GET参数

转义工具URL 编码/解码 - 锤子在线工具

key: json 
value: {"x":"wllm"}
#注意这里需要使用转义字符
value: %7B%22x%22%3A%22wllm%22%7D

设置 Request Headers

Content-Type: application/x-www-form-urlencoded

填写 Request Body ,在body选择form-urlencoded

id=wllmNB

image-20251227130202280

最后的结果为

POST http://node7.anna.nssctf.cn:27699/index.php?json=%7B%22x%22%3A%22wllm%22%7D HTTP/1.1
Host:node7.anna.nssctf.cn:27699
User-Agent:Fiddler-Everywhere
Content-Type:application/x-www-form-urlencoded
Content-Length:9

id=wllmNB

执行得到flag

image-20251227130844556

加密/解密

工具

SageMath

SageMathInstallation Guide

在线工具Sage Cell Server,没有一些基础的库比如说crypto相关,导致无法使用long2bytes,需要手动转换一下

#Ubunbu参考WSL安装方法
#添加环境变量
nano ~/.bashrc
#根据安装目录调整,添加到尾部即可。
#export PATH="/home/wdream/Tools/miniforge3/bin:$PATH"
source ~/.bashrc
conda --version

image-20251230204513382

Ciphey

cyphey命令行工具,需要python3.8.8安装,推荐使用anaconda

安装具体细节参考用Anaconda安装ciphey的过程记录 - 知乎

ciphey -t 4O595954494Q32515046324757595N534R52415653334357474R4N575955544R4O5N4Q46434S4O59474253464Q5N444R4Q51334557524O5N4S424944473542554O595N44534O324R49565746515532464O49345649564O464R4R494543504N35
ciphey -f encrypted.txt

image-20251228201109595

分析过程:

原密文

4O595954494Q32515046324757595N534R52415653334357474R4N575955544R4O5N4Q46434S4O59474253464Q5N444R4Q51334557524O5N4S424944473542554O595N44534O324R49565746515532464O49345649564O464R4R494543504N35

首先统计一下出现的字符集,0~9和NOQRS,字母数量特别少而且是相邻字符,疑似是偏移过的Base16

#!/usr/bin/env python3
import sys
from collections import Counter

def main():
    if len(sys.argv) < 2:
        print("用法: python count_chars.py <字符串>")
        sys.exit(1)

    input_str = sys.argv[1]  # 获取第一个参数作为输入字符串
    counts = Counter(input_str)  # 统计字符出现次数

    # 按字符排序输出
    for char in sorted(counts):
        print(f"'{char}': {counts[char]}")

if __name__ == "__main__":
    main()

image-20251228204307644

NOQRS对应偏移的ABDEF,如果是偏移的则offset = ‘N’ - ‘A’ = 78 - 65 = 13

caesar还原为

4B595954494D32515046324757595A534E52415653334357474E4A575955544E4B5A4D46434F4B59474253464D5A444E4D51334557524B5A4F424944473542554B595A44534B324E49565746515532464B49345649564B464E4E494543504A35

再使用Base16解密得到

KYYTIM2QPF2GWYZSNRAVS3CWGNJWYUTNKZMFCOKYGBSFMZDNMQ3EWRKZOBIDG5BUKYZDSK2NIVWFQU2FKI4VIVKFNNIECPJ5

分析字符集包括部分数字和大写字母,刚好符合Base32的字符集

image-20251228204822526

尝试使用Base32解密得到

V143Pytkc2lAYlV3SlRmVXQ9X0dVdmd6KEYpP3t4V29+MElXSER9TUEkPA==

结尾有两个=,直接猜测是Base64,进行解密得到

W^7?+dsi@bUwJTfUt=_GUvgz(F)?{xWo~0IWHD}MA$<

可以看到这里有很多特殊符号,猜测是Base85或者Base91

如果用91进行解密会直接得到乱码,肯定不正确。

��W*!��H���9��N$� � �xptr�R�`Nl�

但是如果用85进行解密,会提示开头不能是W,这里是因为大部分解密工具选择的算法不对。需要选择RFC1924算法。Base85 Encoder And Decoder

最后得到flag为

image-20251228205450499

Caesar

偏移量

BaseX

X表示的是编码字符数量

Base16

0123456789ABCDEF

没有小写字母,特点只有6种大写字母

Base32

ABCDEFGHIJKLMNOPQRSTUVWXYZ234567

没有小写字母,只有部分数字,缺少数字0 1 8 9

Base58

123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz

没有0

Base62

0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz

Base64

ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=

结尾一般有= 比较好辨别,有大小写数字。

Base85

有多种算法Base85 Encoder And Decoder

包括 Adobe, Ascii, RFC1924, Z85 等

Ascii85 标准(!u

!"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstu

Z85(ZeroMQ 提出):使用完全可打印的 85 个字符,不包括容易混淆的字符(如 "\ 等)

有很多特殊符号

Base91

ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!#$%&()*+,./:;<=>?@[]^_`{|}~"

包括数字,字母,符号,没有反斜杠\,单引号‘

对称加密

AES

非对称加密

RSA

RSA公开密钥密码体制的原理是:根据数论,寻求两个大素数比较简单,而将它们的乘积进行因式分解却极其困难,因此可以将乘积公开作为加密密钥。

  1. 选择两个大素数 p 和 q

  2. 计算: \(n=p×q\) 这里 n 称为 模数,它决定了密钥长度(通常 1024~4096 位)

  3. 计算欧拉函数: \(ϕ(n)=(p−1)(q−1)\)

  4. 选择公钥指数 e,满足: \(1<e<ϕ(n),gcd(e,ϕ(n))=1\) gcd是最大公约数,公钥通常会给出。

  5. 计算私钥指数 d: \(d≡e^{−1}(mod \spaceϕ(n))\)

    (mod ϕ(n))指的是同余,≡是约束关系而不是=,这里-1指的是模逆运算,等价于

    \[\\d*e ≡1 \space (mod \spaceϕ(n)) \\=> \\(d*e) \space mod \spaceϕ(n)=1 \\=> d=inverse\_mod(e,\spaceϕ(n))\]
密钥类型 内容
公钥 (public key) (n, e)
私钥 (private key) (n, d)

加密与解密

  • 加密(用公钥加密消息 m):
\[c=m^e \space mod\space n \\\]
  • 解密(用私钥还原消息 c):
\[m=c^d\space mod\space n \\=>pow(c, d, n)\]

这里 m 要转为整数表示(通常用 bytes_to_long 转换)。

Coppersmith

Coppersmith是指泄露了一部分信息的RSA

PR.<x> = Zmod(n)[] #x是多项式自变量,Zmod(n)表示 多项式的系数是模n的
f = (p_base + (i << 265))  +  x * (mod << 6)
roots = f.monic().small_roots(X=2^453, beta=0.4, epsilon=0.05)
assert roots
p = f(roots[0])

X=2^453表示自变量x取值范围 $[0, 2^{453} - 1]$

beta和epsilon通常不动,epsilon越小表示探索深度越大,花费时间越多。

实例,求出flag

这里是公钥加密信息flag得到加密信息ct

p = getPrime(1024)
q = getPrime(1024)
n = p * q
e = 65537
hint1 = p >> 724
hint2 = q % (2 ** 265)
ct = pow(bytes_to_long(flag), e, n)
print(hint1)
print(hint2)
print(n)
print(ct)

#1514296530850131082973956029074258536069144071110652176122006763622293335057110441067910479
#40812438243894343296354573724131194431453023461572200856406939246297219541329623
#21815431662065695412834116602474344081782093119269423403335882867255834302242945742413692949886248581138784199165404321893594820375775454774521554409598568793217997859258282700084148322905405227238617443766062207618899209593375881728671746850745598576485323702483634599597393910908142659231071532803602701147251570567032402848145462183405098097523810358199597631612616833723150146418889589492395974359466777040500971885443881359700735149623177757865032984744576285054725506299888069904106805731600019058631951255795316571242969336763938805465676269140733371287244624066632153110685509892188900004952700111937292221969
#19073695285772829730103928222962723784199491145730661021332365516942301513989932980896145664842527253998170902799883262567366661277268801440634319694884564820420852947935710798269700777126717746701065483129644585829522353341718916661536894041337878440111845645200627940640539279744348235772441988748977191513786620459922039153862250137904894008551515928486867493608757307981955335488977402307933930592035163126858060189156114410872337004784951228340994743202032248681976932591575016798640429231399974090325134545852080425047146251781339862753527319093938929691759486362536986249207187765947926921267520150073408188188

getPrime是生成指定位数的素数,这里的p q都是1024位的素数。

p >> 724:泄露了 p 的高 1024-724 = 300 位也就是hint1

q % 2^265:泄露了 q 的低 265 位也就是hint2

解密脚本,需要在SageMath环境下运行

from Crypto.Util.number import long_to_bytes
h1, h2 = 1514296530850131082973956029074258536069144071110652176122006763622293335057110441067910479, 40812438243894343296354573724131194431453023461572200856406939246297219541329623
n = 21815431662065695412834116602474344081782093119269423403335882867255834302242945742413692949886248581138784199165404321893594820375775454774521554409598568793217997859258282700084148322905405227238617443766062207618899209593375881728671746850745598576485323702483634599597393910908142659231071532803602701147251570567032402848145462183405098097523810358199597631612616833723150146418889589492395974359466777040500971885443881359700735149623177757865032984744576285054725506299888069904106805731600019058631951255795316571242969336763938805465676269140733371287244624066632153110685509892188900004952700111937292221969
c = 19073695285772829730103928222962723784199491145730661021332365516942301513989932980896145664842527253998170902799883262567366661277268801440634319694884564820420852947935710798269700777126717746701065483129644585829522353341718916661536894041337878440111845645200627940640539279744348235772441988748977191513786620459922039153862250137904894008551515928486867493608757307981955335488977402307933930592035163126858060189156114410872337004784951228340994743202032248681976932591575016798640429231399974090325134545852080425047146251781339862753527319093938929691759486362536986249207187765947926921267520150073408188188
e = 65537
#h1是p的高300位(放在了低位),h2是q的低265位
# 1. 构造 p 的已知部分
mod = 1 << 265 #2^265
p_high = h1 << 724 #将高300位放到高位
#p = h1_724bit
#q = 759bit_h2
#n = p * q
#h2^-1 = inverse_mod(h2, 2^265)
#(n * h2^-1) % mod = (p % mod) * [(759bit_h2 * h2^-1) % mod] = (p % mod) * 1
p_low = (n * inverse_mod(h2, mod)) % mod #得到p的低265位
p_base = p_high + p_low #拼接p,中间有1024-300-265 = 459位缺失(265位~723位)

# 2. 使用 Coppersmith 算法查找 p 的中间缺失位
PR.<x> = Zmod(n)[] #x是多项式自变量,Zmod(n)表示 多项式的系数是模n的
for i in range(64):#2^6 = 64 爆破6位(穷举)
    # 构建多项式: f(x) = (p_base + i*2^265) + x*2^329 (2^329 = mod * 64)
    f = (p_base + (i << 265))  +  x * (mod << 6)
    #注意monic首一化
    roots = f.monic().small_roots(X=2^453, beta=0.4, epsilon =0.05) # 453 = 459 - 6 爆破了6位
    if roots:
        p = int(f(roots[0]))#需要强转int
        break
# 3. 解密 RSA
q = n // p
d = inverse_mod(e, (p - 1) * (q - 1))
print(long_to_bytes(pow(c, d, n)))

执行

sage rsa.py

flag{ef5e1582-8116-4f61-b458-f793dc03f2ff}

如果不手动爆破,即

#...
PR.<x> = Zmod(n)[]
f = p_base + x * mod
roots = f.monic().small_roots(X=2^459, beta=0.4, epsilon =0.04)
assert roots
p = int(f(roots[0]))
#...

epsilon收缩到0.04及以下均可。

Sage Cell Server 在线sage环境不能用long_to_bytes测试得到long56006392793429523039334991295440472601833419964063188654957241090275128167728784831578248304182847101本地转换为bytes

python -c "from Crypto.Util.number import long_to_bytes
print(long_to_bytes(56006392793429523039334991295440472601833419964063188654957241090275128167728784831578248304182847101))"

同样得到flag{ef5e1582-8116-4f61-b458-f793dc03f2ff}

Hastad 广播攻击

CRT中国剩余定理 China Remainder Theorem中国剩余定理 - OI Wiki

「物不知数」问题:有物不知其数,三三数之剩二,五五数之剩三,七七数之剩二。问物几何?

解方程组

image-20260101011010458

得到最小非负整数解$X = x + kN, N=Π^n_{i=1} n_i$

实例

# FLAG = bytes_to_long(pad(b"flag{??????}",64))
# def init_key():
#     p, q = getPrime(512), getPrime(512)
#     n = p*q
#     e = 9
#     while(GCD((p-1)*(q-1),e)!=1):
#         p, q = getPrime(512), getPrime(512)
#         n = p*q
#     d = inverse(e,(p-1)*(q-1))
#     return n,e,d

# n_list=list()
# c_list=list()
# for i in range(9):
#     N,e,d=init_key()
#     n_list.append(N)
#     c=pow(FLAG,e,N)
#     c_list.append(pow(FLAG,e,N))
#     assert(pow(c,d,N)==FLAG)
# print("n_list:",n_list)
# print("c_list:",c_list)

p q都是512bit,n是1024bit,e = 9

后面的部分大致意思就是生成了9组模数n,一共用明文FLAG生成了9组密文c

所以已知c,e,n,记FLAG 为 m,记$N =Π^8_{i=0} n_i$

$$ \c_i = m^e\mod n_i \=> m^e ≡ c_i (\mod n_i)

\m = c^d_i \mod n_i \ \space=> m < n_i => m^9 < N $$ $m^e ≡ c_i (\mod n_i)$可以使用CRT中国剩余定理

crt(c_list, n_list)得到最小非负整数解M,M满足$M ≡ m^e (\mod N)$

但是m^e^ = m^9^ < N 所以m^e^ = M = crt(c_list, n_list)

再开e次根号得到m,也就是9次。

from Crypto.Util.number import long_to_bytes
n_list = [
]

c_list = [
]
e = 9
# CRT 合并
M = crt(c_list, n_list)
# 整数 9 次方根
m = M.nth_root(e)
print(long_to_bytes(int(m)))

MD5

Comments