CTF - no-dorchadas - Cryptography - osugaming.lol

Ссылка на задачу
crypto/no-dorchadas

Описание
i hate dorchadas!!!

Сервер
nc chal.osugaming.lol 9***

Файлы
server.py

Смотрим что содержит файл server.py

server.py

from hashlib import md5
from secret import flag, secret_slider
from base64 import b64encode, b64decode

assert len(secret_slider) == 244
dorchadas_slider = b"0,328,33297,6,0,B|48:323|61:274|61:274|45:207|45:207|63:169|103:169|103:169|249:199|249:199|215:214|205:254,1,450.000017166138,6|6,1:1|2:1,0:0:0:0:"

def sign(beatmap):
    hsh = md5(secret_slider + beatmap)
    return hsh.hexdigest()

def verify(beatmap, signature):
    return md5(secret_slider + beatmap).hexdigest() == signature

def has_dorchadas(beatmap):
    return dorchadas_slider in beatmap

MENU = """
--------------------------
| [1] Sign a beatmap     |
| [2] Verify a beatmap   |
--------------------------"""

def main():
    print("Welcome to the osu! Beatmap Signer")
    while True:
        print(MENU)
        try:
            option = input("Enter your option: ")
            if option == "1":
                beatmap = b64decode(input("Enter your beatmap in base64: "))
                if has_dorchadas(beatmap):
                    print("I won't sign anything with a dorchadas slider in it >:(")
                else:
                    signature = sign(beatmap)
                    print("Okay, I've signed that for you: " + signature)
            elif option == "2":
                beatmap = b64decode(input("Enter your beatmap in base64: "))
                signature = input("Enter your signature for that beatmap: ")
                if verify(beatmap, signature) and has_dorchadas(beatmap):
                    print("How did you add that dorchadas slider?? Anyway, here's a flag: " + flag)
                elif verify(beatmap, signature):
                    print("Signature is valid!")
                else:
                    print("Signature is invalid :(")
        except:
            print("An error occurred!")
            exit(-1)

main()

Сервер вычисляет signature для beatmap, не содержашего маркер (dorchadas).
Для получения флага нужно ввести beatmap, содержаший маркер и signature.

signature вычисляется md5 алгоритмом из строки secret_slider + beatmap.

py

def sign(beatmap):
    hsh = md5(secret_slider + beatmap)
    return hsh.hexdigest()

Посмотрим как реализован алгоритм md5 в стандартной библиотеке crypto/md5 языка go

md5.go

type digest struct {
	s   [4]uint32
	x   [BlockSize]byte
	nx  int
	len uint64
}

func Sum(data []byte) [Size]byte {
	var d digest
	d.Reset()
	d.Write(data)
	return d.checkSum()
}

func (d *digest) Reset() {
	d.s[0] = init0
	d.s[1] = init1
	d.s[2] = init2
	d.s[3] = init3
	d.nx = 0
	d.len = 0
}

func (d *digest) Write(p []byte) (nn int, err error) {
	// Note that we currently call block or blockGeneric
	// directly (guarded using haveAsm) because this allows
	// escape analysis to see that p and d don't escape.
	nn = len(p)
	d.len += uint64(nn)
	if d.nx > 0 {
		n := copy(d.x[d.nx:], p)
		d.nx += n
		if d.nx == BlockSize {
			if haveAsm {
				block(d, d.x[:])
			} else {
				blockGeneric(d, d.x[:])
			}
			d.nx = 0
		}
		p = p[n:]
	}
	if len(p) >= BlockSize {
		n := len(p) &^ (BlockSize - 1)
		if haveAsm {
			block(d, p[:n])
		} else {
			blockGeneric(d, p[:n])
		}
		p = p[n:]
	}
	if len(p) > 0 {
		d.nx = copy(d.x[:], p)
	}
	return
}

func (d *digest) checkSum() [Size]byte {
	// Append 0x80 to the end of the message and then append zeros
	// until the length is a multiple of 56 bytes. Finally append
	// 8 bytes representing the message length in bits.
	//
	// 1 byte end marker :: 0-63 padding bytes :: 8 byte length
	tmp := [1 + 63 + 8]byte{0x80}
	pad := (55 - d.len) % 64                             // calculate number of padding bytes
	binary.LittleEndian.PutUint64(tmp[1+pad:], d.len<<3) // append length in bits
	d.Write(tmp[:1+pad+8])

	// The previous write ensures that a whole number of
	// blocks (i.e. a multiple of 64 bytes) have been hashed.
	if d.nx != 0 {
		panic("d.nx != 0")
	}

	var digest [Size]byte
	binary.LittleEndian.PutUint32(digest[0:], d.s[0])
	binary.LittleEndian.PutUint32(digest[4:], d.s[1])
	binary.LittleEndian.PutUint32(digest[8:], d.s[2])
	binary.LittleEndian.PutUint32(digest[12:], d.s[3])
	return digest
}

func blockGeneric(dig *digest, p []byte) {
	// load state
	a, b, c, d := dig.s[0], dig.s[1], dig.s[2], dig.s[3]

	for i := 0; i <= len(p)-BlockSize; i += BlockSize {
		// eliminate bounds checks on p
		q := p[i:]
		q = q[:BlockSize:BlockSize]

		// save current state
		aa, bb, cc, dd := a, b, c, d

		// load input block
		x0 := binary.LittleEndian.Uint32(q[4*0x0:])
		x1 := binary.LittleEndian.Uint32(q[4*0x1:])
		x2 := binary.LittleEndian.Uint32(q[4*0x2:])
		x3 := binary.LittleEndian.Uint32(q[4*0x3:])
		x4 := binary.LittleEndian.Uint32(q[4*0x4:])
		x5 := binary.LittleEndian.Uint32(q[4*0x5:])
		x6 := binary.LittleEndian.Uint32(q[4*0x6:])
		x7 := binary.LittleEndian.Uint32(q[4*0x7:])
		x8 := binary.LittleEndian.Uint32(q[4*0x8:])
		x9 := binary.LittleEndian.Uint32(q[4*0x9:])
		xa := binary.LittleEndian.Uint32(q[4*0xa:])
		xb := binary.LittleEndian.Uint32(q[4*0xb:])
		xc := binary.LittleEndian.Uint32(q[4*0xc:])
		xd := binary.LittleEndian.Uint32(q[4*0xd:])
		xe := binary.LittleEndian.Uint32(q[4*0xe:])
		xf := binary.LittleEndian.Uint32(q[4*0xf:])

		// round 1
		a = b + bits.RotateLeft32((((c^d)&b)^d)+a+x0+0xd76aa478, 7)
		d = a + bits.RotateLeft32((((b^c)&a)^c)+d+x1+0xe8c7b756, 12)
		c = d + bits.RotateLeft32((((a^b)&d)^b)+c+x2+0x242070db, 17)
		b = c + bits.RotateLeft32((((d^a)&c)^a)+b+x3+0xc1bdceee, 22)
		a = b + bits.RotateLeft32((((c^d)&b)^d)+a+x4+0xf57c0faf, 7)
		d = a + bits.RotateLeft32((((b^c)&a)^c)+d+x5+0x4787c62a, 12)
		c = d + bits.RotateLeft32((((a^b)&d)^b)+c+x6+0xa8304613, 17)
		b = c + bits.RotateLeft32((((d^a)&c)^a)+b+x7+0xfd469501, 22)
		a = b + bits.RotateLeft32((((c^d)&b)^d)+a+x8+0x698098d8, 7)
		d = a + bits.RotateLeft32((((b^c)&a)^c)+d+x9+0x8b44f7af, 12)
		c = d + bits.RotateLeft32((((a^b)&d)^b)+c+xa+0xffff5bb1, 17)
		b = c + bits.RotateLeft32((((d^a)&c)^a)+b+xb+0x895cd7be, 22)
		a = b + bits.RotateLeft32((((c^d)&b)^d)+a+xc+0x6b901122, 7)
		d = a + bits.RotateLeft32((((b^c)&a)^c)+d+xd+0xfd987193, 12)
		c = d + bits.RotateLeft32((((a^b)&d)^b)+c+xe+0xa679438e, 17)
		b = c + bits.RotateLeft32((((d^a)&c)^a)+b+xf+0x49b40821, 22)

		// round 2
		a = b + bits.RotateLeft32((((b^c)&d)^c)+a+x1+0xf61e2562, 5)
		d = a + bits.RotateLeft32((((a^b)&c)^b)+d+x6+0xc040b340, 9)
		c = d + bits.RotateLeft32((((d^a)&b)^a)+c+xb+0x265e5a51, 14)
		b = c + bits.RotateLeft32((((c^d)&a)^d)+b+x0+0xe9b6c7aa, 20)
		a = b + bits.RotateLeft32((((b^c)&d)^c)+a+x5+0xd62f105d, 5)
		d = a + bits.RotateLeft32((((a^b)&c)^b)+d+xa+0x02441453, 9)
		c = d + bits.RotateLeft32((((d^a)&b)^a)+c+xf+0xd8a1e681, 14)
		b = c + bits.RotateLeft32((((c^d)&a)^d)+b+x4+0xe7d3fbc8, 20)
		a = b + bits.RotateLeft32((((b^c)&d)^c)+a+x9+0x21e1cde6, 5)
		d = a + bits.RotateLeft32((((a^b)&c)^b)+d+xe+0xc33707d6, 9)
		c = d + bits.RotateLeft32((((d^a)&b)^a)+c+x3+0xf4d50d87, 14)
		b = c + bits.RotateLeft32((((c^d)&a)^d)+b+x8+0x455a14ed, 20)
		a = b + bits.RotateLeft32((((b^c)&d)^c)+a+xd+0xa9e3e905, 5)
		d = a + bits.RotateLeft32((((a^b)&c)^b)+d+x2+0xfcefa3f8, 9)
		c = d + bits.RotateLeft32((((d^a)&b)^a)+c+x7+0x676f02d9, 14)
		b = c + bits.RotateLeft32((((c^d)&a)^d)+b+xc+0x8d2a4c8a, 20)

		// round 3
		a = b + bits.RotateLeft32((b^c^d)+a+x5+0xfffa3942, 4)
		d = a + bits.RotateLeft32((a^b^c)+d+x8+0x8771f681, 11)
		c = d + bits.RotateLeft32((d^a^b)+c+xb+0x6d9d6122, 16)
		b = c + bits.RotateLeft32((c^d^a)+b+xe+0xfde5380c, 23)
		a = b + bits.RotateLeft32((b^c^d)+a+x1+0xa4beea44, 4)
		d = a + bits.RotateLeft32((a^b^c)+d+x4+0x4bdecfa9, 11)
		c = d + bits.RotateLeft32((d^a^b)+c+x7+0xf6bb4b60, 16)
		b = c + bits.RotateLeft32((c^d^a)+b+xa+0xbebfbc70, 23)
		a = b + bits.RotateLeft32((b^c^d)+a+xd+0x289b7ec6, 4)
		d = a + bits.RotateLeft32((a^b^c)+d+x0+0xeaa127fa, 11)
		c = d + bits.RotateLeft32((d^a^b)+c+x3+0xd4ef3085, 16)
		b = c + bits.RotateLeft32((c^d^a)+b+x6+0x04881d05, 23)
		a = b + bits.RotateLeft32((b^c^d)+a+x9+0xd9d4d039, 4)
		d = a + bits.RotateLeft32((a^b^c)+d+xc+0xe6db99e5, 11)
		c = d + bits.RotateLeft32((d^a^b)+c+xf+0x1fa27cf8, 16)
		b = c + bits.RotateLeft32((c^d^a)+b+x2+0xc4ac5665, 23)

		// round 4
		a = b + bits.RotateLeft32((c^(b|^d))+a+x0+0xf4292244, 6)
		d = a + bits.RotateLeft32((b^(a|^c))+d+x7+0x432aff97, 10)
		c = d + bits.RotateLeft32((a^(d|^b))+c+xe+0xab9423a7, 15)
		b = c + bits.RotateLeft32((d^(c|^a))+b+x5+0xfc93a039, 21)
		a = b + bits.RotateLeft32((c^(b|^d))+a+xc+0x655b59c3, 6)
		d = a + bits.RotateLeft32((b^(a|^c))+d+x3+0x8f0ccc92, 10)
		c = d + bits.RotateLeft32((a^(d|^b))+c+xa+0xffeff47d, 15)
		b = c + bits.RotateLeft32((d^(c|^a))+b+x1+0x85845dd1, 21)
		a = b + bits.RotateLeft32((c^(b|^d))+a+x8+0x6fa87e4f, 6)
		d = a + bits.RotateLeft32((b^(a|^c))+d+xf+0xfe2ce6e0, 10)
		c = d + bits.RotateLeft32((a^(d|^b))+c+x6+0xa3014314, 15)
		b = c + bits.RotateLeft32((d^(c|^a))+b+xd+0x4e0811a1, 21)
		a = b + bits.RotateLeft32((c^(b|^d))+a+x4+0xf7537e82, 6)
		d = a + bits.RotateLeft32((b^(a|^c))+d+xb+0xbd3af235, 10)
		c = d + bits.RotateLeft32((a^(d|^b))+c+x2+0x2ad7d2bb, 15)
		b = c + bits.RotateLeft32((d^(c|^a))+b+x9+0xeb86d391, 21)

		// add saved state
		a += aa
		b += bb
		c += cc
		d += dd
	}

	// save state
	dig.s[0], dig.s[1], dig.s[2], dig.s[3] = a, b, c, d
}

В методе Write происходит обработка входной строки блоками по 64 байта.
Есть внутреннее состояние (четыре 32-битных числа - dig.s[0], dig.s[1], dig.s[2], dig.s[3]), которое меняется после обработки очередного блока.

Из этих же чисел и строится хэш:

go

func (d *digest) checkSum() [Size]byte {
    ...
    var digest [Size]byte
    binary.LittleEndian.PutUint32(digest[0:], d.s[0])
    binary.LittleEndian.PutUint32(digest[4:], d.s[1])
    binary.LittleEndian.PutUint32(digest[8:], d.s[2])
    binary.LittleEndian.PutUint32(digest[12:], d.s[3])
    return digest
}

В конце добавляется буффер содержащий размер исходной строки и заполнитель из нулей до границы блока в 64 байта:

go

func (d *digest) checkSum() [Size]byte {
    ...
    tmp := [1 + 63 + 8]byte{0x80}
    pad := (55 - d.len) % 64                             // calculate number of padding bytes
    binary.LittleEndian.PutUint64(tmp[1+pad:], d.len<<3) // append length in bits
    d.Write(tmp[:1+pad+8])
    ...
}

Не забываем про выравнивание в 64 байта.

Значит, зная размер secret_slider, можно точно сказать какой заполнитель будет вычислен

План:

  • Выбираем произвольный beatmap0, не содержащий dorchadas_slider, чтобы длина secret_slider+beatmap0 была кратна 64
  • Отправляем beatmap0 на сервер, получаем signature0
  • Выбираем новый beatmap1 как beatmap0 + padding0 + dorchadas_slider + padding1
    • padding0 - заполнитель, который был бы вычислен для secret_slider+beatmap
    • padding1 - произвольный заполнитель, чтобы общая длина строки былка кратна 64
  • Создаем новую структуру md5.hash с внутренним состоянием dig.s[0], dig.s[1], dig.s[2], dig.s[3] из signature0
  • Добавляем dorchadas_slider + padding1, получаем md5 хэш, который и будет искомым signature1

В соответствии с планом напишем программу на языке Go

Самое сложно тут правильно вычислить заполнители.
И очень неочевиден был момент инициализации hash структуры нужным состоянием.

main.go

package main

import (
	"crypto/md5"
	"encoding"
	"encoding/base64"
	"encoding/binary"
	"encoding/hex"
	"fmt"
	"hash"
	"slices"
	"strings"
)

var (
	secret_slider    = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
	dorchadas_slider = "0,328,33297,6,0,B|48:323|61:274|61:274|45:207|45:207|63:169|103:169|103:169|249:199|249:199|215:214|205:254,1,450.000017166138,6|6,1:1|2:1,0:0:0:0:"
)

func sign(beatmap string) string {
	hsh := md5.Sum([]byte(secret_slider + beatmap))
	return hex.EncodeToString(hsh[:])
}

func verify(beatmap, signature string) bool {
	hsh := md5.Sum([]byte(secret_slider + beatmap))
	return hex.EncodeToString(hsh[:]) == signature
}

func has_dorchadas(beatmap string) bool {
	return strings.Contains(beatmap, dorchadas_slider)
}

func hashFromSum(hashStr string, ll int) hash.Hash {
	hash := md5.New()

	md5Bytes, err := hex.DecodeString(hashStr)
	if err != nil {
		panic(err)
	}

	if len(md5Bytes) != hash.Size() {
		panic("invalid md5 len")
	}

	state, err := hash.(encoding.BinaryMarshaler).MarshalBinary()
	if err != nil {
		panic(err)
	}

	slices.Reverse(md5Bytes[0 : 0+4])
	slices.Reverse(md5Bytes[4 : 4+4])
	slices.Reverse(md5Bytes[8 : 8+4])
	slices.Reverse(md5Bytes[12 : 12+4])

	copy(state[4:], md5Bytes)

	copy(state[len(state)-8:], binary.BigEndian.AppendUint64(nil, uint64(ll)))

	err = hash.(encoding.BinaryUnmarshaler).UnmarshalBinary(state)
	if err != nil {
		panic(err)
	}

	return hash
}

func main() {
	secretPadding := strings.Repeat("_", 64-len(secret_slider)%64)
	dorchadasWithPadding := dorchadas_slider + strings.Repeat("_", 64-len(dorchadas_slider)%64)

	getPaddingBuffer := func(ll int) []byte {
		tmp := [1 + 63 + 8]byte{0x80}
		pad := (55 - uint64(ll)) % 64
		binary.LittleEndian.PutUint64(tmp[1+pad:], (uint64(ll))<<3)
		fmt.Println("padding", ll, hex.EncodeToString(tmp[:1+pad+8]))
		return tmp[:1+pad+8]
	}

	fmt.Printf("secret_slider: %v %q\n", len(secret_slider), secret_slider)
	fmt.Println("")

	beatmap0 := secretPadding + strings.Repeat(".", 64)
	signature0 := sign(beatmap0)
	fmt.Printf("beatmap0 str: %q\n", beatmap0)
	fmt.Printf("beatmap0 base64: %q\n", base64.StdEncoding.EncodeToString([]byte(beatmap0)))
	fmt.Printf("has_dorchadas: %v\n", has_dorchadas(beatmap0))
	fmt.Printf("signature0: %v\n", signature0)
	fmt.Print("\n")

	{
		padding := string(getPaddingBuffer(len(secret_slider) + len(beatmap0)))
		beatmap1 := beatmap0 + padding + dorchadasWithPadding

		h := hashFromSum(signature0, len(secret_slider)+len(beatmap0)+len(padding))
		h.Write([]byte(dorchadasWithPadding))
		signature1 := hex.EncodeToString(h.Sum(nil)[:])

		fmt.Printf("beatmap1 str: %q\n", beatmap1)
		fmt.Printf("beatmap1 base64: %q\n", base64.StdEncoding.EncodeToString([]byte(beatmap1)))
		fmt.Printf("has_dorchadas: %v\n", has_dorchadas(beatmap1))
		fmt.Printf("signature1: %v\n", signature1)
		fmt.Printf("sign(): %v\n", sign(beatmap1))
		fmt.Printf("verify(): %v\n", verify(beatmap1, signature1))
	}
}

Запускаем программу:

output

[amyasnikov@ubuntu:~]$ go run main.go

secret_slider: 244 "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"

beatmap0 str: "____________................................................................"
beatmap0 base64: "X19fX19fX19fX19fLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLg=="
has_dorchadas: false
signature0: 329b5c185bffbb153faf53d9ed584050

padding 320 8000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000
beatmap1 str: "____________................................................................\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\n\x00\x00\x00\x00\x00\x000,328,33297,6,0,B|48:323|61:274|61:274|45:207|45:207|63:169|103:169|103:169|249:199|249:199|215:214|205:254,1,450.000017166138,6|6,1:1|2:1,0:0:0:0:_____________________________________________"
beatmap1 base64: "X19fX19fX19fX19fLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLoAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAoAAAAAAAAwLDMyOCwzMzI5Nyw2LDAsQnw0ODozMjN8NjE6Mjc0fDYxOjI3NHw0NToyMDd8NDU6MjA3fDYzOjE2OXwxMDM6MTY5fDEwMzoxNjl8MjQ5OjE5OXwyNDk6MTk5fDIxNToyMTR8MjA1OjI1NCwxLDQ1MC4wMDAwMTcxNjYxMzgsNnw2LDE6MXwyOjEsMDowOjA6MDpfX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX18="
has_dorchadas: true
signature1: bb063ae11a317be1c2e825a6bbc7e4e0
sign(): bb063ae11a317be1c2e825a6bbc7e4e0
verify(): true

Видим, что удалось получить правильный signature1 без знания secret_slider, зная только длину secret_slider, beatmap0 и signature0.

Подключаемся к серверу. Вводим beatmap0. Получаем signature0.

output

[amyasnikov@ubuntu:~]$ nc chal.osugaming.lol 9***
Welcome to the osu! Beatmap Signer

--------------------------
| [1] Sign a beatmap     |
| [2] Verify a beatmap   |
--------------------------
Enter your option: 1
Enter your beatmap in base64: X19fX19fX19fX19fLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLg==
Okay, I've signed that for you: 5cea859a5d3186608d5e210b2ef920bd

Эмулируем вычисление signature0, явно задав значение, которые прислал сервер.

diff

  beatmap0 := secretPadding + strings.Repeat(".", 64)
  signature0 := sign(beatmap0)
+ signature0 = "5cea859a5d3186608d5e210b2ef920bd"
  fmt.Printf("beatmap0 str: %q\n", beatmap0)
  fmt.Printf("beatmap0 base64: %q\n", base64.StdEncoding.EncodeToString([]byte(beatmap0)))

Запускаем программу:

output

[amyasnikov@ubuntu:~]$ go run main.go

secret_slider: 244 "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"

beatmap0 str: "____________................................................................"
beatmap0 base64: "X19fX19fX19fX19fLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLg=="
has_dorchadas: false
signature0: 5cea859a5d3186608d5e210b2ef920bd

padding 320 8000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000
beatmap1 str: "____________................................................................\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\n\x00\x00\x00\x00\x00\x000,328,33297,6,0,B|48:323|61:274|61:274|45:207|45:207|63:169|103:169|103:169|249:199|249:199|215:214|205:254,1,450.000017166138,6|6,1:1|2:1,0:0:0:0:_____________________________________________"
beatmap1 base64: "X19fX19fX19fX19fLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLoAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAoAAAAAAAAwLDMyOCwzMzI5Nyw2LDAsQnw0ODozMjN8NjE6Mjc0fDYxOjI3NHw0NToyMDd8NDU6MjA3fDYzOjE2OXwxMDM6MTY5fDEwMzoxNjl8MjQ5OjE5OXwyNDk6MTk5fDIxNToyMTR8MjA1OjI1NCwxLDQ1MC4wMDAwMTcxNjYxMzgsNnw2LDE6MXwyOjEsMDowOjA6MDpfX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX18="
has_dorchadas: true
signature1: 2dd237915047bb057d18e7f31b6f73a0
sign(): bb063ae11a317be1c2e825a6bbc7e4e0
verify(): false

Валидация прошла неуспено, так как мы подменили signature0.
Вычисленный signature1, указываем серверу для валидации beatmap1.

output

[amyasnikov@ubuntu:~]$ nc chal.osugaming.lol 9***
Welcome to the osu! Beatmap Signer

--------------------------
| [1] Sign a beatmap     |
| [2] Verify a beatmap   |
--------------------------
Enter your option: 1
Enter your beatmap in base64: X19fX19fX19fX19fLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLg==
Okay, I've signed that for you: 5cea859a5d3186608d5e210b2ef920bd

--------------------------
| [1] Sign a beatmap     |
| [2] Verify a beatmap   |
--------------------------
Enter your option: 2
Enter your beatmap in base64: X19fX19fX19fX19fLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLoAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAoAAAAAAAAwLDMyOCwzMzI5Nyw2LDAsQnw0ODozMjN8NjE6Mjc0fDYxOjI3NHw0NToyMDd8NDU6MjA3fDYzOjE2OXwxMDM6MTY5fDEwMzoxNjl8MjQ5OjE5OXwyNDk6MTk5fDIxNToyMTR8MjA1OjI1NCwxLDQ1MC4wMDAwMTcxNjYxMzgsNnw2LDE6MXwyOjEsMDowOjA6MDpfX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX18=
Enter your signature for that beatmap: 2dd237915047bb057d18e7f31b6f73a0
How did you add that dorchadas slider?? Anyway, here's a flag: osu{********************}

P.S. Часть данных заменена на звездочки

Похожее