CTF - no-dorchadas - Cryptography - osugaming.lol
1 Исходные данные
Ссылка на задачу
crypto/no-dorchadas
Описание
i hate dorchadas!!!
Сервер
nc chal.osugaming.lol 9***
Файлы
server.py
2 Решение
2.1 Анализ исходных данных
Смотрим что содержит файл 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.
def sign(beatmap):
hsh = md5(secret_slider + beatmap)
return hsh.hexdigest()
2.2 Анализ md5 алгоритма
Посмотрим как реализован алгоритм md5 в стандартной библиотеке crypto/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]
), которое меняется после обработки очередного блока.
Из этих же чисел и строится хэш:
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 байта:
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
2.3 Проверка алгоритма на тестовых данных
В соответствии с планом напишем программу на языке Go
Самое сложно тут правильно вычислить заполнители.
И очень неочевиден был момент инициализации hash структуры нужным состоянием.
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))
}
}
Запускаем программу:
[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
.
2.4 Получение флага
Подключаемся к серверу. Вводим beatmap0
. Получаем signature0
.
[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
, явно задав значение, которые прислал сервер.
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)))
Запускаем программу:
[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
.
[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{********************}
3 Полезные ресурсы
P.S. Часть данных заменена на звездочки