В комментариях к статье /threads/1167/ появилась идея о создании ос, в которой будет использован этот механизм, однако в приведённом виде, это не имеет никакого смысла.
TL;DR:
Критически важные файлы хранятся на удалённом сервере. При вводе специальных паролей эти данные автоматически удаляются, а сервер отправляет сигнал, что владелец скомпрометирован. Вместо настоящих файлов пользователю выдаются фековые данные, которые можно показать под давлением.
Основная проблема заключается в том, что ключ шифрования от ОС может быть перехвачен, а после, расшифрован локально.
То есть, после извлечения диска, можно выяснить как работает шифрование, а после, подставить уже полученные данные и заставить человека ввести пароль от них.
Ниже я расскажу о том, как предотвратить эту и другие проблемы.
Сперва я переписал вебсервер под telnet:
# DMSserver.py
import socketserver, hashlib, os, base64
PRT = 8000
def save_hsh(FN, TPL, CNT=99999):
HSH = [hashlib.sha1(f"{TPL}{i}".encode()).hexdigest() for i in range(CNT)]
open(FN, "w").write("\n".join(HSH))
return HSH
GRP = {
"pass1.txt": {"FIL": "1.txt", "TPL": "pass"},
"pass2.txt": {"FIL": "2.txt", "TPL": "spass"}
}
ERS = {"TPL": "bpass"}
for K, V in GRP.items():
V["HSH"] = save_hsh(K, V["TPL"])
ERS["HSH"] = save_hsh("pass_erase.txt", ERS["TPL"])
def gen_hsh(PWD, LEN):
H, S = "", "ecstasy" + PWD
for _ in range(LEN):
D = hashlib.sha512(S.encode()).hexdigest()
H += D[0]
S = D[1:129]
return H
def enc_fil(FIL, PWD):
B = base64.b32encode(open(FIL, "rb").read()).decode()
H = gen_hsh(PWD, len(B) * 2)
return "".join(
f"{(int(C, 16) + int(H[I], 16)) % 16:x}"
for I, C in enumerate("".join(f"{ord(X):02x}" for X in B))
).encode()
class T(socketserver.BaseRequestHandler):
def handle(self):
self.request.settimeout(5)
data = b""
while True:
byte = self.request.recv(1)
if not byte or byte in (b'\r', b'\n'):
break
data += byte
PWD = data.decode().strip()
for PWD_FIL, V in GRP.items():
if PWD in V["HSH"]:
V["HSH"].remove(PWD)
open(PWD_FIL, "w").write("\n".join(V["HSH"]))
if os.path.isfile(V["FIL"]):
self.request.sendall(enc_fil(V["FIL"], PWD))
return
if PWD in ERS["HSH"]:
os.system("echo 'ПарольОтRoot' | sudo -S bash DMS.sh")
socketserver.TCPServer.allow_reuse_address = True
socketserver.TCPServer(("", PRT), T).serve_forever()
Первым делом не стоит сразу же разглашать адрес, с которым будет проходить коммуникация. Вряд ли конечный сервер взломают, однако лучше обезопасить себя от рисков.
Я предлагаю разбить скрипт на две части, где в первой части будет введен локальный пароль для расшифровки второй, где приложен механизм шифрования и адрес сервера.
Так как адрес сервера находится в оверлей сети типа tor, узнать его адрес через сниффинг трафика не должно быть возможным.
Для шифрования скрипта я предлагаю использовать моё шифрование, оно достаточно надёжное. Скорость можно изменить, добавив большее колво байт за хеш.
# cryptor.sh
read -p "Действие (e/d): " act
read -p "Файл: " file
read -s -p "Пароль: " pass
echo
gen_hash() {
sec="$pass"; hash=""
for ((i=0;i<$1;i++)); do
h=$(echo -n "$sec" | sha512sum | awk '{print $1}')
hash+=${h:0:1}
sec=${h:1:128}
done
echo "$hash"
}
if [[ "$act" == "e" ]]; then
hex=$(base32 -w 0 "$file" | od -An -tx1 | tr -d ' \n')
hash=$(gen_hash ${#hex})
enc=""
for ((i=0;i<${#hex};i++)); do
c=$((0x${hex:i:1}+0x${hash:i:1}))
((c>15)) && c=$((c-16))
enc+=$(printf "%x" $c)
done
echo "$enc" > enc.txt
echo "Готово! Принимай enc.txt"
elif [[ "$act" == "d" ]]; then
hex=$(tr -d '\n' < "$file")
hash=$(gen_hash ${#hex})
dec=""
for ((i=0;i<${#hex};i++)); do
c=$((0x${hex:i:1}-0x${hash:i:1}))
((c<0)) && c=$((c+16))
dec+=$(printf "%x" $c)
done
echo "$dec" | xxd -r -p | base32 -d > dec.txt
echo "Готово! Принимай dec.txt"
else
echo "Буквочка 'e' для шифрования, а 'd' для дешифровки."
fi
Хранение расшифрованного скрипта необходимо проводить в оперативной памяти, иначе это не будет иметь никакого смысла, ведь данные на диске будут прочитаны.
# tui.sh
#!/usr/bin/env bash
clear
rows=$(tput lines); cols=$(tput cols)
title="Внешний пароль"
tput cup $((rows/2-1)) $(((cols-${#title})/2)); printf "%s\n" "$title"
tput cup $((rows/2+1)) $(((cols-${#title})/2))
pass=""
while IFS= read -r -s -n 1 char; do
[[ $char == $'\0' || $char == $'\n' || $char == $'\r' ]] && break
[[ $char == $'\177' ]] && { (( ${#pass} > 0 )) && pass=${pass%?} && printf '\b \b'; continue; }
pass+=$char; printf '*'
done
file=enc.txt
echo
gen_hash() {
sec="$pass"; hash=""
for ((i=0;i<$1;i++)); do
h=$(echo -n "$sec" | sha512sum | awk '{print $1}')
hash+=${h:0:1}
sec=${h:1:128}
done
echo "$hash"
}
hex=$(tr -d '\n' < "$file")
hash=$(gen_hash ${#hex})
dec=""
for ((i=0;i<${#hex};i++)); do
c=$((0x${hex:i:1}-0x${hash:i:1}))
((c<0)) && c=$((c+16))
dec+=$(printf "%x" $c)
done
# Запуск в ram
PID=$$; echo "$dec" | xxd -r -p | base32 -d > /dev/shm/$PID.sh chmod +x /dev/shm/$PID.sh && exec /dev/shm/$PID.sh; rm /dev/shm/$PID.sh
Вторая причина использовать подобную меру заклюаеться в том, что криптоанализ требует значительного колва времени. Этим можно воспользоваться.
Например, если владелец легитимно не взаимодействовал с сервером, ключи шифрования будут удалены.
Это на тот случай, если пароли не заставят вводить сразу на месте.
Во втором скрипте необходимо предоставить причину, почему скрипт вообще был зашифрован.
Как вариант - проверка на подмену одной программы, которая в будущем будет необходима для генерации ключа (ниже объясню)
origexp="937a92774f545dd85c916329ef97e62ee6e9672abd0f25ce73faf36f32f3ddee4ce08d65c2ce1c7ffa61e8657a76bc01f660f63b93ed0349a1663a9b66bc802f"
[[ $(sha512sum brutecrypt|cut -d' ' -f1) == $origexp ]] && title="Подмены содержимого нет" || title="Подмены содержимого нет!!"
После, как и в /threads/1167/ ввод пароля (вебсервер изменён на telnet)
#!/bin/bash
clear
rows=$(tput lines); cols=$(tput cols)
origexp="937a92774f545dd85c916329ef97e62ee6e9672abd0f25ce73faf36f32f3ddee4ce08d65c2ce1c7ffa61e8657a76bc01f660f63b93ed0349a1663a9b66bc802f"
[[ $(sha512sum brutecrypt|cut -d' ' -f1) == $origexp ]] && title="Подмены содержимого нет" || title="Подмены содержимого нет!!"
tput cup $((rows/2-1)) $(((cols-${#title})/2)); printf "%s\n" "$title"
tput cup $((rows/2+1)) $(((cols-${#title})/2))
pass=""
while IFS= read -r -s -n 1 char; do
[[ $char == $'\0' || $char == $'\n' || $char == $'\r' ]] && break
[[ $char == $'\177' ]] && { (( ${#pass} > 0 )) && pass=${pass%?} && printf '\b \b'; continue; }
pass+=$char; printf '*'
done
H=$(echo -n "$pass"| sha1sum | cut -d' ' -f1)
#Используйте proxychains для проксирования, если используете tor
hex=$((echo $H; echo)|nc xxxxxxxxxxxxxxx.onion 8000 | tr -d '\n')
hash=""; sec="ecstasy$H"
for((i=0;i<${#hex};i++));do
h=$(echo -n "$sec"|sha512sum|awk '{print $1}')
hash+="${h:0:1}"
sec="${h:1:128}"
done
dec=""; for((i=0;i<${#hex};i++));do
c=$((0x${hex:$i:1}-0x${hash:$i:1}))
((c < 0)) && c=$((c+16))
dec+=$(printf "%x" $c)
done
input=$(echo "$dec" | xxd -r -p | base32 -d | sha512sum | cut -d' ' -f1)
Теперь поговорим про brutecrypt.
Это можно назвать защитой от перебора. Программа должна сильно повысить энтропию.
По сути это немного изменённый Proof of Work
Есть изначальная строка и префикс.
Из строки берёться sha512 и добовляеться число и вычисляться sha512, который при следущей итерации будет использован как изначальная строка
Это происходит до тех пор пока префикс не будет соответствовать запрошенному.
// brutecrypt (Модифицировал PoW)
#include <stdio.h>
#include <string.h>
#include <openssl/evp.h>
#include <stdint.h>
int main(int c, char **v) {
char b[512], h[129];
strcpy(b, v[1]);
size_t s = strlen(b);
char *p = v[2];
uint64_t n = 0;
while (1) {
sprintf(b + s, "%lx", n);
EVP_MD_CTX *ctx = EVP_MD_CTX_new();
EVP_DigestInit_ex(ctx, EVP_sha512(), NULL);
EVP_DigestUpdate(ctx, b, strlen(b));
unsigned char d[64];
unsigned int l = 64;
EVP_DigestFinal_ex(ctx, d, &l);
EVP_MD_CTX_free(ctx);
for (int i = 0; i < 64; i++)
sprintf(h + i * 2, "%02x", d[i]);
h[128] = 0;
if (!strncmp(h, p, strlen(p))) {
EVP_MD_CTX *ctx2 = EVP_MD_CTX_new();
EVP_DigestInit_ex(ctx2, EVP_sha512(), NULL);
EVP_DigestUpdate(ctx2, b, strlen(b));
unsigned char f[64];
unsigned int fl = 64;
EVP_DigestFinal_ex(ctx2, f, &fl);
EVP_MD_CTX_free(ctx2);
EVP_MD_CTX *ctx3 = EVP_MD_CTX_new();
EVP_DigestInit_ex(ctx3, EVP_sha512(), NULL);
EVP_DigestUpdate(ctx3, f, 64);
unsigned char ff[64];
unsigned int ffl = 64;
EVP_DigestFinal_ex(ctx3, ff, &ffl);
EVP_MD_CTX_free(ctx3);
for (int i = 0; i < 64; i++)
printf("%02x", ff[i]);
puts("");
return 0;
}
memcpy(b, d, 64); b[64] = 0; s = 64; // Убери эту строку и будет обычный PoW
}
}
Дополнительно, уже в сам скрипт я добавил раунды. Всё тоже самое, только при первой итерации будет взят полученный хеш из прошлого раунда.
В финализации из последнего хеша будет взят... хеш. Это необходимо лишь для того, чтобы сокрыть префикс и повысить криптостойкость.
clear
rows=$(tput lines); cols=$(tput cols)
title="Теперь секретное число: "
tput cup $((rows/2-1)) $(((cols-${#title})/2)); printf "%s\n" "$title"
tput cup $((rows/2+1)) $(((cols-${#title})/2))
pass=""
while IFS= read -r -s -n 1 char; do
[[ $char == $'\0' || $char == $'\n' || $char == $'\r' ]] && break
[[ $char == $'\177' ]] && { (( ${#pass} > 0 )) && pass=${pass%?} && printf '\b \b'; continue; }
pass+=$char; printf '*'
done
clear
echo $input
sleep 100
brute=$(( (pass + pass % 2) / 2 )); brute=${brute:0:2}
for ((i=1; i<=brute; i++)); do
input=$(./brutecrypt "$input" "$pass")
echo "Раунд $i/$brute: $input"
done
input=$(echo -n $input | sha512sum | cut -d' ' -f1)
echo "Финализация: $input"
Ура! Теперь есть ключ который будет использован для шифрования.
Предвартельно получите как минимум два ключа ключа (один для двойного дна а второй для фейковой системы)
Теперь переходим к созданию ОС!
В примере я буду поднимать систему на arch linux.
Сперва разбейте диск на три раздела, первый под efi, второй 4-6гб под лаунчер а третий для зашифрованной системы.
sgdisk --new=1:0:+512M --typecode=1:ef00 --new=2:0:+6G --typecode=2:8300 --new=3:0:0 --typecode=3:8300 /dev/диск
На втором разделе будет линукс, который будет получать ключ из сервера и проводить с ним все операции для запуска системы из третьего раздела.
Я должен предупредить, что зашифрованная система после запуска не будет изолирована от лаунчера после запуска. Это не критически важно но вы должны это знать.
kexec может помочь вам с этим, однако в отличии от luks, он не будет работать с veracrypt о котором далее пойдет речь.
Далее во избежание путаницы я буду называть систему которая запускает зишифрованную ос - лаунчером.
Установка системы лаунчера:
# Вы можете использовать этот гайд для установки базовой системы (https://mags.zone/help/arch-usb.html)
sudo mkfs.fat -F32 /dev/диск1
sudo mkfs.ext4 -F /dev/диск2
mkdir /mnt/outer
sudo mount /dev/sdb2 /mnt/outer
sudo mkdir -p /mnt/outer/boot/efi
sudo mount /dev/sdb1 /mnt/outer/boot/efi
sudo pacstrap /mnt/outer base linux linux-firmware pacman
genfstab -U /mnt/outer | sudo tee /mnt/outer/etc/fstab
sudo arch-chroot /mnt/outer
pacman -S efibootmgr veracrypt networkmanager curl grub veracrypt device-mapper linux-headers mkinitcpio
ln -sf /usr/share/zoneinfo/Antarctica/South_Pole /etc/localtime
hwclock --systohc
echo "en_US.UTF-8 UTF-8" > /etc/locale.gen
locale-gen
echo "LANG=ru_RU.UTF-8" > /etc/locale.conf
echo "Anna's iPhone" > /etc/hostname # :)
systemctl enable NetworkManager
grub-install --target=x86_64-efi --efi-directory=/boot/efi --bootloader-id=GRUB --removable
grub-mkconfig -o /boot/grub/grub.cfg
# Автологин
mkdir -p /etc/systemd/system/[email protected]/
echo -e '[Service]\nExecStart=\nExecStart=-/sbin/agetty --autologin root --noclear %I $TERM' > /etc/systemd/system/[email protected]/override.conf
systemctl enable [email protected]
# GRUB без логов, интерактива и ожидания
sed -i 's/GRUB_TIMEOUT=.*/GRUB_TIMEOUT=0/;s/GRUB_TIMEOUT_STYLE=.*/GRUB_TIMEOUT_STYLE=hidden/;s|GRUB_CMDLINE_LINUX_DEFAULT=.*|GRUB_CMDLINE_LINUX_DEFAULT="quiet loglevel=0 systemd.show_status=auto rd.udev.log_priority=3 vt.global_cursor_default=0 splash"|' /etc/default/grub
grub-mkconfig -o /boot/grub/grub.cfg
Далее дважды выполните
veracrypt -t -c /dev/диск
В первый раз создайте Normal type, а во второй Hidden.
Укажите ранее полученные пароли.
Желательно, чтобы размер Hidden был настолько маленьким, насколько это позволяют потребности, однако не стоит переусердствовать, ведь его размер расширить нельзя.
После создания, стоит заполнить Normal type всем чем попало.
Используйте эту команду чтобы загрузить данные с другого диска
rsync -aAXv --progress /mnt/old/ /mnt/vc
После этого, монтируйтесь в двойное дно и настройте систему из которой будете работать.
Осталось закончить пару последних штрихов, в конец второго скрипта добавьте:
# Если PIM не задан - опустите флаг
echo -n "$input" | veracrypt --non-interactive --pim $pass --stdin --mount /dev/nvme0n1p3 /mnt/vc;
mount /dev/mapper/veracrypt1 /mnt/vc
mkdir -p /mnt/vc/{dev/pts,proc,sys,run}
mount --bind /dev /mnt/vc/dev
mount --bind /dev/pts /mnt/vc/dev/pts
mount --bind /proc /mnt/vc/proc
mount --bind /sys /mnt/vc/sys
mount --bind /run /mnt/vc/run
arch-chroot /mnt/vc /bin/bash -c "sddm" # Запуск графического окружения
Переместите файлы: brutecrypt, enc.txt и tui.sh на лаучер, а так же задайте автоматическое выполнение tui.sh при входе в систему.
Все действия связанные с ключами и адресом проводите изолированно от этой системы, на лаунчере должны быть лишь эти файлы.
Видео демонстрация входа в систему
Клирнет видео:
Если у вас есть предложения по улучшению или замечания - я постараюсь на всё ответить!
Специально для forum.LIMB.cc
LIMB