Open4

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 (ゼタバイト)....そんな容量ないかもなぁ...................................