MindBlowing - PascalCTF-2025 - Crypto

1 Исходные данные
Описание
My friend Marco recently dived into studying bitwise operators, and now he’s convinced he’s invented pseudorandom numbers!
Could you help me figure out his secrets?
Файлы
mind-blowing.py
TCP сервер
0.0.0.0:420
2 Решение
2.1 Пример взаимодействия с сервером
Пример:
❯ nc 0.0.0.0 420
Welcome to the italian MindBlowing game!
1. Generate numbers
2. Exit
> 1
Gimme the index of a sentence: 2
Gimme the number of seeds: 3
Seed of the number 1: 100
Seed of the number 2: 5678
Seed of the number 3: 123456789
Result: [100, 4652, 104939797]
Welcome to the italian MindBlowing game!
1. Generate numbers
2. Exit
> 2
2.2 Анализ скрипта mind-blowing.py
Содержимое файла mind-blowing.py
:
import signal, os
SENTENCES = [
b"Elia recently passed away, how will we be able to live without a sysadmin?!!?",
os.urandom(42),
os.getenv('FLAG', 'pascalCTF{REDACTED}').encode()
]
def generate(seeds: list[int], idx: int) -> list[int]:
result = []
if idx < 0 or idx > 2:
return result
encoded = int.from_bytes(SENTENCES[idx], 'big')
for bet in seeds:
# why you're using 1s when 0s exist
if bet.bit_count() > 40:
continue
result.append(encoded & bet)
return result
def menu():
print("Welcome to the italian MindBlowing game!")
print("1. Generate numbers")
print("2. Exit")
print()
return input('> ')
def handler(signum, frame):
print("Time's up!")
exit()
if __name__ == '__main__':
signal.signal(signal.SIGALRM, handler)
signal.alarm(300)
while True:
choice = menu()
try:
if choice == '1':
idx = int(input('Gimme the index of a sentence: '))
seeds_num = int(input('Gimme the number of seeds: '))
seeds = []
for _ in range(seeds_num):
seeds.append(int(input(f'Seed of the number {_+1}: ')))
print(f"Result: {generate(seeds, idx)}")
elif choice == '2':
break
else:
print("Wrong choice (。_。)")
except:
print("Boh ㄟ( ▔, ▔ )ㄏ")
Функция generate
:
def generate(seeds: list[int], idx: int) -> list[int]:
result = []
if idx < 0 or idx > 2:
return result
encoded = int.from_bytes(SENTENCES[idx], 'big')
for bet in seeds:
# why you're using 1s when 0s exist
if bet.bit_count() > 40:
continue
result.append(encoded & bet)
return result
Самое интересное в файле - это функциия generate
.
Функция generate
применяет операцию AND
к каждому значению seed и SENTENCES[idx].
Ограничение: в seed
должно быть не более 40 битов, выставленных в значение 1.
2.3 Алгоритм получения флага
Известно, что A AND 1 == A
, поэтому использование чисел seed в формате 0x00...00 FFFFFFFFFF 00...00
.
Это позволит на каждое значение seed
узнать 40 бит значений строки SENTENCES[idx]
.
Изобразить можно так:
flag = 0x <p_flag_N> ... <p_flag_2> <p_flag_1> <p_flag_0>
AND
seed = 0x 0000000000 ... 0000000000 FFFFFFFFFF 0000000000
=
result = 0x 0000000000 ... 0000000000 <p_flag_1> 0000000000
После применения функции generage
получим список частичных значений строки SENTENCES[idx]
.
Останется только применить операцию OR чтобы получить исходное значение:
0x 0000000000 ... 0000000000 0000000000 <p_flag_0>
OR
0x 0000000000 ... 0000000000 <p_flag_1> 0000000000
OR
0x 0000000000 ... <p_flag_2> 0000000000 0000000000
OR
0x <p_flag_N> ... 0000000000 0000000000 0000000000
=
0x <p_flag_N> ... <p_flag_2> <p_flag_1> <p_flag_0>
2.4 Написание программы для решения задачи
Напишем программу на языке Go.
Основная часть алгоритма:
seed := big.NewInt(0xFFFFFFFFFF)
for range seeds {
_ = readAll(conn, "Seed of the number")
write(conn, seed.String(), "\n")
seed.Lsh(seed, 5*8)
}
result := readAll(conn, `Result: \[(.*)\]`)
write(conn, "2", "\n")
resultList := strings.Split(result[0], ", ")
numResult := big.NewInt(0)
for _, num := range resultList {
bigNum, _ := new(big.Int).SetString(num, 10)
numResult.Or(numResult, bigNum)
}
Вспомогательные функции для взаимодействия с tcp
сервером:
func readAll(conn net.Conn, pattern string) []string {
if pattern == "" {
pattern = ".*"
}
err := conn.SetReadDeadline(time.Now().Add(1 * time.Second))
checkErr(err)
var buf bytes.Buffer
bufTmp := make([]byte, 1024)
defer func() {
fmt.Printf("%s", buf.String())
}()
for {
w, err := conn.Read(bufTmp)
if err != nil {
break
}
buf.Write(bufTmp[:w])
re := regexp.MustCompile(pattern)
res := re.FindStringSubmatch(buf.String())
if len(res) > 0 {
return res[1:]
}
}
return nil
}
func write(conn net.Conn, msgs ...string) {
for _, msg := range msgs {
fmt.Printf("%s", msg)
_, err := conn.Write([]byte(msg))
checkErr(err)
}
}
Программа целиком:
package main
import (
"bytes"
"fmt"
"log"
"math/big"
"net"
"regexp"
"strings"
"time"
)
func checkErr(err error) {
if err != nil {
log.Fatal(err)
}
}
func readAll(conn net.Conn, pattern string) []string {
if pattern == "" {
pattern = ".*"
}
err := conn.SetReadDeadline(time.Now().Add(1 * time.Second))
checkErr(err)
var buf bytes.Buffer
bufTmp := make([]byte, 1024)
defer func() {
fmt.Printf("%s", buf.String())
}()
for {
w, err := conn.Read(bufTmp)
if err != nil {
break
}
buf.Write(bufTmp[:w])
re := regexp.MustCompile(pattern)
res := re.FindStringSubmatch(buf.String())
if len(res) > 0 {
return res[1:]
}
}
return nil
}
func write(conn net.Conn, msgs ...string) {
for _, msg := range msgs {
fmt.Printf("%s", msg)
_, err := conn.Write([]byte(msg))
checkErr(err)
}
}
func main() {
seeds := 15
conn, err := net.Dial("tcp", "0.0.0.0:420")
checkErr(err)
defer conn.Close()
_ = readAll(conn, ">")
write(conn, "1", "\n")
_ = readAll(conn, "Gimme the index of a sentence:")
write(conn, "2", "\n")
_ = readAll(conn, "Gimme the number of seeds:")
write(conn, fmt.Sprintf("%d", seeds), "\n")
seed := big.NewInt(0xFFFFFFFFFF)
for range seeds {
_ = readAll(conn, "Seed of the number")
write(conn, seed.String(), "\n")
seed.Lsh(seed, 5*8)
}
result := readAll(conn, `Result: \[(.*)\]`)
write(conn, "2", "\n")
resultList := strings.Split(result[0], ", ")
numResult := big.NewInt(0)
for _, num := range resultList {
bigNum, _ := new(big.Int).SetString(num, 10)
numResult.Or(numResult, bigNum)
}
log.Printf("sequence: %q", numResult.Bytes())
}
2.5 Получение флага
Запускаем программу:
❯ go run ./main.go
Welcome to the italian MindBlowing game!
1. Generate numbers
2. Exit
> 1
Gimme the index of a sentence: 2
Gimme the number of seeds: 15
Seed of the number 1: 1099511627775
Seed of the number 2: 1208925819613529663078400
Seed of the number 3: 1329227995783706947084192431105638400
Seed of the number 4: 1461501637329573690207899916843379212595652198400
Seed of the number 5: 1606938044257528773904631189422958917689486710763136902758400
Seed of the number 6: 1766847064776777391539038510467376553735142734273096755127823408457318400
Seed of the number 7: 1942668892223962223854683522493935609141663920536312037354936790780782679003915878400
Seed of the number 8: 2135987035918967413502795977098632652695881003450010363107989468300967913370901645737756878438400
Seed of the number 9: 2348542582771697240853559686706942005669512996793717166184934318762261983506786223737918357834745740944998400
Seed of the number 10: 2582249878084560047073145338775122393732916455801847830281751036669940331045308985604530744836674132437518403269715558400
Seed of the number 11: 2839213766777132166330209215972861793146908553310506467181741867036541559836419990370916950656009282618448462193041354380896790118400
Seed of the number 12: 3121748550313153017614817515376958009624035624952652239293972123452397167729216247901113142425499037367976292193434894295423498421735415768678400
Seed of the number 13: 3432398830062183108940634407309315011404924483765904106554566758441912434739202654322460054710606971385584663724580602087439587376962873930607725860251238400
Seed of the number 14: 3773962424818108953411489276130777940517380523807805710726134647540926930662556080494427897151597487721137647753275196744074844200906231638561575929526409029503837798400
Seed of the number 15: 4149515568877218996087586322338919596431457963351519978775240889262276605112662522237314900095017667563020227843759320807364387230512004723256950041481402252809749107357860128358400
Result: [340615582589, 540105707313557011431424, 573534362623358518682916943843819520, 276623010158214588041355735867023681176211554304, 730698794427646327503939589001659032615029466500461097910272, 719167047433024526231091425152901363773606853613508115403079644113534976, 891073434186835764057640516389499118009852274186379612748823597801037001983424200704, 411952256289209711872940994910171510442900979836054977464109624318060731134988146047128107483136, 480576598041647900457435649952942661009413411970906414757589920243911407026452746149274391194373294592622592, 679143826994024558223059644340021127316436266546390704520513006965329811396281382413145839880122728042600282803318816768, 1080794800379284484265221729794839512767299489986780271478711492910601751755170761455678011238079027056998233048323529105087509037056, 317991941879328014615329165951001983779718095300701651373151962141529688549611591455208584687271559932833603202741452845921388212518912, 0, 0, 0]
Welcome to the italian MindBlowing game!
1. Generate n2
2025/03/28 18:36:47 sequence: "pascalCTF{m4by3_1_sh0uld_ch3ck_th3_t0t4l_numb3r_0f_ONES}"
Получили флаг pascalCTF{m4by3_1_sh0uld_ch3ck_th3_t0t4l_numb3r_0f_ONES}