Open4
Rainbow Table攻撃をしてみたい
下の記事がRainbow Tableの意義とか特徴について分かりやすく書いてる。還元関数の作り方が知りたい
GPT-4に書いてもらった
長いので折り畳み
package main
import (
"crypto/sha256"
"encoding/hex"
"fmt"
"math/rand"
"os"
"sync"
"time"
)
const (
chainLength = 5000
numberOfChains = 100000
charset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
rainbowTableFile = "rainbow_table_sha256.txt"
goroutineNum = 10
)
func randomString(length int) string {
b := make([]byte, length)
for i := range b {
b[i] = charset[rand.Int63()%int64(len(charset))]
}
return string(b)
}
func reduce(hash string, pos int) string {
var value int64
for _, r := range hash {
value += int64(r)
}
rand.Seed(value + int64(pos))
return randomString(6)
}
func createChain(start string) string {
current := start
for i := 0; i < chainLength; i++ {
hash := sha256.Sum256([]byte(current))
hashStr := hex.EncodeToString(hash[:])
current = reduce(hashStr, i)
}
return current
}
func worker(id int, chainsToGenerate int, wg *sync.WaitGroup, results chan<- string) {
defer wg.Done()
for i := 0; i < chainsToGenerate; i++ {
start := randomString(6)
end := createChain(start)
results <- fmt.Sprintf("%s:%s\n", start, end)
if i%1000 == 0 {
fmt.Printf("Worker %d generated %d chains\n", id, i)
}
}
}
func main() {
rand.Seed(time.Now().UnixNano())
f, err := os.Create(rainbowTableFile)
if err != nil {
panic(err)
}
defer f.Close()
chainsPerWorker := numberOfChains / goroutineNum
results := make(chan string, numberOfChains)
var wg sync.WaitGroup
wg.Add(goroutineNum)
for i := 0; i < goroutineNum; i++ {
go worker(i, chainsPerWorker, &wg, results)
}
go func() {
wg.Wait()
close(results)
}()
for chain := range results {
_, err := f.WriteString(chain)
if err != nil {
panic(err)
}
}
fmt.Println("Rainbow table created successfully.")
}
なぜか全部ハッシュが変な感じになってしまった GPT-4に直してもらってもいいけど普通に全通り列挙する方法をやってみる
全通り保存するための容量を試算してみた。以下の要件を満たすパスワードを:
で区切って64文字のハッシュと連結して保管することを考える。パスワード同士は改行(LF)で区切る。
- パスワードは8~14文字
- 英小文字、英大文字、数字、記号(
#.$*-=_
) のみ使用可能 - 英小文字、英大文字、数字を必ず入れる必要がある
size = 0
for password_length in range(8, 15):
combination = (26+26+10+7) ** password_length - (26+10+7) ** password_length * 2 - (26+26+7) ** password_length + (26+7) ** password_length * 2 + (10+7) ** password_length
dsize = combination * (password_length + 1 + 64 + 1)
print('dsize', password_length, dsize)
size += dsize
print('size', str(size) + 'B')
size //= 1024
print('size', str(size) + 'KB')
size //= 1024
print('size', str(size) + 'MB')
size //= 1024
print('size', str(size) + 'GB')
size //= 1024
print('size', str(size) + 'TB')
size //= 1024
print('size', str(size) + 'PB')
size //= 1024
print('size', str(size) + 'EB')
size //= 1024
print('size', str(size) + 'ZB')
実行結果
dsize 8 25634438365465034
dsize 9 1940763626229600525
dsize 10 144013883268590422924
dsize 11 10539300521474486258651
dsize 12 763662869158440945919758
dsize 13 54928516669400690035574953
dsize 14 3928852213320326672068419920
size 3984555078139688610721661765B
size 3891167068495789658907872KB
size 3799967840327919588777MB
size 3710906094070233973GB
size 3623931732490462TB
size 3538995832510PB
size 3456050617EB
size 3375049ZB
300万ZB (ゼタバイト)....そんな容量ないかもなぁ...................................