Bagaimana mencegah proses menulis file

13

Saya ingin menjalankan perintah di Linux dengan cara yang tidak dapat membuat atau membuka file apa pun untuk ditulis. Seharusnya masih dapat membaca file seperti biasa (jadi chroot kosong bukan pilihan), dan masih dapat menulis ke file yang sudah terbuka (terutama stdout).

Poin bonus jika menulis file ke direktori tertentu (yaitu direktori saat ini) masih dimungkinkan.

Saya mencari solusi yang merupakan proses-lokal, yaitu tidak melibatkan konfigurasi hal-hal seperti AppArmor atau SELinux untuk seluruh sistem, atau root privilege. Ini mungkin melibatkan pemasangan modul kernel mereka.

Saya melihat kemampuan dan ini akan bagus dan mudah, jika ada kemampuan untuk membuat file. ulimit adalah pendekatan lain yang nyaman, jika mencakup use case ini.

Joachim Breitner
sumber
Terlalu banyak program menganggap bahwa mereka dapat menulis file sebagai hal yang biasa (dan gagal dengan cara yang aneh ketika mereka tidak bisa). stracememberi tahu Anda file apa yang sedang dibuka oleh program. Mengapa Anda ingin melakukan ini? Apakah ini program khusus, atau Anda ingin ini untuk pengujian atau yang lainnya? Bisakah Anda menjalankan program sebagai pengguna / grup yang tidak memiliki izin untuk menulis hampir di mana-mana kecuali di direktori saat ini? Distribusi Linux modern menggunakan ide grup untuk setiap pengguna, jadi ini seharusnya relatif mudah diatur.
vonbrand
Ini adalah program khusus (Isabelle) yang mengartikan kode dengan cara yang agak aman (tidak ada eksekusi kode arbitrer), tetapi masih memungkinkan kode untuk membuat file di tempat-tempat sewenang-wenang. Karena kode tidak dipercaya, saya ingin mencegah hal ini terjadi (dengan membatalkan program). Program ini sudah berjalan sebagai pengguna khusus, tetapi saya akan merasa lebih aman jika kodenya tidak bisa naik, katakanlah, / tmp atau tempat-tempat serupa.
Joachim Breitner
Anda dapat menambahkan pengguna baru, untuk menjalankan aplikasi.
ctrl-alt-delor

Jawaban:

9

Bagaimana dengan membuat chroot kosong, lalu mengikat-mount filesystem utama sebagai read-only di dalam chroot?

Mungkin akan menjadi seperti ini untuk membuat bind-mount read-only:

mount --bind /foo/ /path/to/chroot/
mount -o remount,ro /path/to/chroot/

Anda dapat mengikat direktori lain yang Anda ingin memiliki akses tulis bagi penjara. Berhati-hatilah jika Anda perlu untuk me-mount direktori khusus (/ dev /, / proc /, / sys /), memasang mereka apa adanya mungkin tidak aman.

Lie Ryan
sumber
Sekali lagi, perlu hak akses root dan "pengaturan global" lainnya. Tapi sebuah pilihan, ya.
Joachim Breitner
Apakah /foo/jalur ke sistem file utama?
Wayne Conrad
5

Tampaknya alat yang tepat untuk pekerjaan ini adalah fseccompBerdasarkan pada sync-ignoringkode f oleh Bastian Blank, saya membuat file yang relatif kecil ini yang menyebabkan semua anak-anaknya tidak dapat membuka file untuk menulis:

/*
 * Copyright (C) 2013 Joachim Breitner <[email protected]>
 *
 * Based on code Copyright (C) 2013 Bastian Blank <[email protected]>
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice, this
 *    list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright notice,
 *    this list of conditions and the following disclaimer in the documentation
 *    and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#define _GNU_SOURCE 1
#include <errno.h>
#include <fcntl.h>
#include <seccomp.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#define filter_rule_add(action, syscall, count, ...) \
  if (seccomp_rule_add(filter, action, syscall, count, ##__VA_ARGS__)) abort();

static int filter_init(void)
{
  scmp_filter_ctx filter;

  if (!(filter = seccomp_init(SCMP_ACT_ALLOW))) abort();
  if (seccomp_attr_set(filter, SCMP_FLTATR_CTL_NNP, 1)) abort();
  filter_rule_add(SCMP_ACT_ERRNO(EACCES), SCMP_SYS(open), 1, SCMP_A1(SCMP_CMP_MASKED_EQ, O_WRONLY, O_WRONLY));
  filter_rule_add(SCMP_ACT_ERRNO(EACCES), SCMP_SYS(open), 1, SCMP_A1(SCMP_CMP_MASKED_EQ, O_RDWR, O_RDWR));
  return seccomp_load(filter);
}

int main(__attribute__((unused)) int argc, char *argv[])
{
  if (argc <= 1)
  {
    fprintf(stderr, "usage: %s COMMAND [ARG]...\n", argv[0]);
    return 2;
  }

  if (filter_init())
  {
    fprintf(stderr, "%s: can't initialize seccomp filter\n", argv[0]);
    return 1;
  }

  execvp(argv[1], &argv[1]);

  if (errno == ENOENT)
  {
    fprintf(stderr, "%s: command not found: %s\n", argv[0], argv[1]);
    return 127;
  }

  fprintf(stderr, "%s: failed to execute: %s: %s\n", argv[0], argv[1], strerror(errno));
  return 1;
}

Di sini Anda dapat melihat bahwa masih mungkin untuk membaca file:

[jojo@kirk:1] Wed, der 06.03.2013 um 12:58 Uhr Keep Smiling :-)
> ls test
ls: cannot access test: No such file or directory
> echo foo > test
bash: test: Permission denied
> ls test
ls: cannot access test: No such file or directory
> touch test
touch: cannot touch 'test': Permission denied
> head -n 1 no-writes.c # reading still works
/*

Itu tidak mencegah menghapus file, atau memindahkannya, atau operasi file lain selain membuka, tetapi itu bisa ditambahkan.

Alat yang memungkinkan ini tanpa harus menulis kode C adalah syscall_limiter .

Joachim Breitner
sumber
4
Perhatikan bahwa pendekatan aman adalah untuk syscalls daftar putih, bukan untuk daftar hitam mereka. Jika terlalu banyak ditolak, bantuan eksternal yang tidak dikotak kotak dapat digunakan untuk membantu program. Dengan LD_PRELOAD, bantuan semacam itu dapat dibuat transparan untuk program yang kami jalankan.
Vi.
4

Apakah Anda ingin menulis pengganti open(…)fungsi, dan memuatnya menggunakan LD_PRELOAD?

Leonid
sumber
2
Maksud Anda mungkin open... Ya, saya akan mempertimbangkan untuk menggunakan solusi yang ada yang menggunakan pendekatan ini, ya.
Joachim Breitner
2
Ada sesuatu seperti ini di github.com/certik/restrict , tetapi dikonfigurasi dengan kompilasi dan tampaknya tidak digunakan secara luas.
Joachim Breitner
Ya, maaf, kesalahan saya, memperbarui jawaban ... Tapi menurut saya Anda harus mengganti satu untuk write(…)juga.
Leonid
Sedangkan untuk github.com/certik/restrict , ya, Anda sepenuhnya benar.
Leonid
3

Solusi paling sederhana mungkin adalah program pembungkus yang menciptakan namespace filesystem baru dengan mount filesystem yang relevan read-only dan kemudian mengeksekusi program yang Anda coba batasi.

Inilah yang systemddilakukan ketika Anda menggunakan ReadOnlyDirectories=untuk menandai direktori tertentu sebagai hanya-baca untuk layanan. Ada juga unshareperintah util-linuxyang dapat melakukan pekerjaan menciptakan namespace baru, sehingga Anda dapat melakukan sesuatu seperti:

unshare -m <wrapper>

di mana wrapperkemudian hanya perlu memount ulang sistem file seperti yang diperlukan sebelum memulai program target nyata.

Satu-satunya masalah adalah Anda harus rootmenciptakan namespace baru ...

TomH
sumber
Saya memikirkan hal ini. Tetapi apakah ini mungkin tanpa menjadi root? Apakah ada skrip / program siap pakai untuk itu tersedia?
Joachim Breitner
1
Ya, tampaknya Anda harus melakukan root, setidaknya dengan kernel 3.7.
TomH
Saya melihat lebih jauh pada solusi ini. Dimungkinkan untuk mengikat secara mount / ke yang baru /, tetapi tidak dan menandainya secara rekursif sebagai hanya-baca.
Joachim Breitner
2

Anda dapat menjalankannya di chroot, memasang versi khusus /tmp dan bagian dalamnya. Mungkin systemd sangat membantu, dan khususnya systemd-nspawn (1) , yang terlihat seperti yang Anda inginkan.

vonbrand
sumber
2

Sebuah mesin virtual akan memungkinkan skrip untuk menulis di mana saja tanpa mempengaruhi sistem host, dan untuk memeriksa ke mana ia sebenarnya mencoba menulis, yang tampaknya menjadi tujuan.

Misalnya, Anda dapat dengan mudah memulai Arch Linux

kvm -boot d -m 512 -cdrom archlinux-*.iso
l0b0
sumber
1
Saya masih ingin menjalankan program di mesin saat ini untuk menghindari keharusan mengatur sistem baru, lingkungan baru dll. Mesin virtual terlalu berat untuk kasus penggunaan saya.
Joachim Breitner
2

Melakukan beberapa pengaturan awal sebagai root adalah cara termudah. Secara khusus, chroot menjadi bind mount read-only adalah jalur dengan resistansi paling rendah.

Anda dapat menggunakan bindf alih-alih mount --bindmembuat tampilan baca-saja tanpa harus menjadi root. Namun, Anda perlu melakukan sesuatu sebagai root untuk mencegah akses ke file lain, seperti chroot.

Pendekatan lain adalah ke LD_PRELOADperpustakaan yang menghubungkan ke dalam pembukaan file dan menolak untuk mengizinkan penulisan. Ini tidak memerlukan hak istimewa. Dari perspektif keamanan, ini dapat di-bypass, tetapi tidak masalah untuk kasus penggunaan Anda di mana Anda hanya perlu memuat fitur tertentu dan bukan kode asli yang arbitrer. Namun, saya tidak tahu perpustakaan yang ada untuk ini. LD_PRELOADjuga dapat digunakan untuk membatasi program ke tampilan hanya baca yang dibuat dengan mount --bindatau bindfs; lagi, saya tidak tahu perpustakaan yang ada.

Pada Debian dan turunannya, Anda dapat mengatur lingkungan schroot . Schroot adalah root setuid dan perlu dikonfigurasi sebagai root, tetapi dapat dijalankan oleh pengguna yang berwenang.

Metode yang tidak memerlukan kerja sama dari root adalah menjalankan proses di mesin virtual. Anda dapat mengatur KVM atau VirtualBox, atau mode pengguna Linux . Ini agak kelas berat, dan akan berarti konsumsi memori tambahan, tetapi seharusnya tidak mempengaruhi kecepatan perhitungan simbolik mentah secara signifikan.

Bagaimana cara "memenjarakan" suatu proses tanpa menjadi root? mungkin memberikan beberapa inspirasi.

Gilles 'SANGAT berhenti menjadi jahat'
sumber
1

Salah satu cara untuk setidaknya mencegah proses menulis file (tetapi tidak membuatnya) adalah dengan menelepon ulimit -f 0terlebih dahulu. Ini akan membatalkan proses segera setelah mencoba menulis ke file, tetapi membuat file kosong masih mungkin.

Joachim Breitner
sumber